package org.gudy.azureus2.core3.peer.impl.control;

///*
// * Created by Olivier Chalouhi
// * Modified Apr 13, 2004 by Alon Rohter
// * Heavily modified Sep 2005 by Joseph Bridgewater
// * Copyright (C) 2004, 2005, 2006 Aelitis, All Rights Reserved.
// *
// * This program is free software; you can redistribute it and/or
// * modify it under the terms of the GNU General Public License
// * as published by the Free Software Foundation; either version 2
// * of the License, or (at your option) any later version.
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// * GNU General Public License for more details.
// * You should have received a copy of the GNU General Public License
// * along with this program; if not, write to the Free Software
// * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
// *
// * AELITIS, SAS au capital de 46,603.30 euros
// * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
// */
//
//package org.gudy.azureus2.core3.peer.impl.control;
//
//
//import java.net.Inet6Address;
//import java.net.InetAddress;
//import java.net.InetSocketAddress;
//import java.net.UnknownHostException;
//import java.nio.ByteBuffer;
//import java.util.*;
//
//import org.gudy.azureus2.core3.config.COConfigurationManager;
//import org.gudy.azureus2.core3.config.ParameterListener;
//import org.gudy.azureus2.core3.disk.*;
//import org.gudy.azureus2.core3.ipfilter.*;
//import org.gudy.azureus2.core3.logging.*;
//import org.gudy.azureus2.core3.peer.*;
//import org.gudy.azureus2.core3.peer.impl.*;
//import org.gudy.azureus2.core3.peer.util.PeerIdentityDataID;
//import org.gudy.azureus2.core3.peer.util.PeerIdentityManager;
//import org.gudy.azureus2.core3.peer.util.PeerUtils;
//import org.gudy.azureus2.core3.torrent.TOTorrentException;
//import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncer;
//import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerResponse;
//import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerResponsePeer;
//import org.gudy.azureus2.core3.tracker.client.TRTrackerScraperResponse;
//import org.gudy.azureus2.core3.util.*;
//import org.gudy.azureus2.plugins.download.DownloadAnnounceResultPeer;
//import org.gudy.azureus2.plugins.peers.Peer;
//import org.gudy.azureus2.plugins.peers.PeerDescriptor;
//
////import com.aelitis.azureus.core.networkmanager.LimitedRateGroup;
////import com.aelitis.azureus.core.networkmanager.admin.NetworkAdmin;
//import com.aelitis.azureus.core.networkmanager.impl.tcp.TCPConnectionManager;
//import com.aelitis.azureus.core.networkmanager.impl.tcp.TCPNetworkManager;
//import com.aelitis.azureus.core.networkmanager.impl.udp.UDPNetworkManager;
//import com.aelitis.azureus.core.peermanager.control.PeerControlInstance;
//import com.aelitis.azureus.core.peermanager.control.PeerControlScheduler;
//import com.aelitis.azureus.core.peermanager.control.PeerControlSchedulerFactory;
//import com.aelitis.azureus.core.peermanager.nat.PeerNATInitiator;
//import com.aelitis.azureus.core.peermanager.nat.PeerNATTraversalAdapter;
//import com.aelitis.azureus.core.peermanager.nat.PeerNATTraverser;
//import com.aelitis.azureus.core.peermanager.peerdb.*;
//import com.aelitis.azureus.core.peermanager.piecepicker.PiecePicker;
//import com.aelitis.azureus.core.peermanager.piecepicker.PiecePickerFactory;
//import com.aelitis.azureus.core.peermanager.unchoker.Unchoker;
//import com.aelitis.azureus.core.peermanager.unchoker.UnchokerFactory;
//import com.aelitis.azureus.core.peermanager.unchoker.UnchokerUtil;
//import com.aelitis.azureus.core.peermanager.uploadslots.UploadHelper;
//import com.aelitis.azureus.core.peermanager.uploadslots.UploadSlotManager;
//import com.aelitis.azureus.core.util.FeatureAvailability;
//import com.aelitis.azureus.core.util.bloom.BloomFilter;
//import com.aelitis.azureus.core.util.bloom.BloomFilterFactory;
//
///**
// * manages all peer transports for a torrent
// *
// * @author MjrTom
// *			2005/Oct/08: Numerous changes for new piece-picking. Also
// *						a few optimizations and multi-thread cleanups
// *			2006/Jan/02: refactoring piece picking related code
// */
//
//@SuppressWarnings("serial")
//public class
//PEPeerControlImpl
////extends LogRelation
//implements 	PEPeerControl, ParameterListener, DiskManagerWriteRequestListener, PeerControlInstance,
////        PeerNATInitiator,
//DiskManagerCheckRequestListener
////        , IPFilterListener
//{
////	private static final LogIDs LOGID = LogIDs.PEER;
//
//	private static final int WARNINGS_LIMIT = 2;
//
//	private static final int	CHECK_REASON_DOWNLOADED		= 1;
//	private static final int	CHECK_REASON_COMPLETE		= 2;
//	private static final int	CHECK_REASON_SCAN			= 3;
//	private static final int	CHECK_REASON_SEEDING_CHECK		= 4;
//	private static final int	CHECK_REASON_BAD_PIECE_CHECK	= 5;
//
//	private static final int	SEED_CHECK_WAIT_MARKER	= 65526;
//
//		// config
//
//	private static boolean 	disconnect_seeds_when_seeding;
//	private static boolean 	enable_seeding_piece_rechecks;
//	private static int		stalled_piece_timeout;
//	private static boolean 	fast_unchoke_new_peers;
//    private static float	ban_peer_discard_ratio;
//    private static int		ban_peer_discard_min_kb;
//    private static boolean	udp_fallback_for_failed_connection;
//    private static boolean	udp_fallback_for_dropped_connection;
//    private static boolean	udp_probe_enabled;
//    private static boolean	hide_a_piece;
//    private static boolean	prefer_udp_default;
//
//	static{
//
//		COConfigurationManager.addAndFireParameterListeners(
//			new String[]{
//				"Disconnect Seed",
//				"Seeding Piece Check Recheck Enable",
//				"peercontrol.stalled.piece.write.timeout",
//				"Peer.Fast.Initial.Unchoke.Enabled",
//   				"Ip Filter Ban Discard Ratio",
//   				"Ip Filter Ban Discard Min KB",
//   				"peercontrol.udp.fallback.connect.fail",
//   				"peercontrol.udp.fallback.connect.drop",
//   				"peercontrol.udp.probe.enable",
//   				"peercontrol.hide.piece",
//   				"peercontrol.prefer.udp",
//			},
//			new ParameterListener()
//			{
//				public void
//				parameterChanged(
//					String name )
//				{
//					disconnect_seeds_when_seeding 		= COConfigurationManager.getBooleanParameter("Disconnect Seed");
//					enable_seeding_piece_rechecks 		= COConfigurationManager.getBooleanParameter("Seeding Piece Check Recheck Enable");
//					stalled_piece_timeout				= COConfigurationManager.getIntParameter( "peercontrol.stalled.piece.write.timeout", 60*1000 );
//					fast_unchoke_new_peers 				= COConfigurationManager.getBooleanParameter( "Peer.Fast.Initial.Unchoke.Enabled" );
//					ban_peer_discard_ratio				= COConfigurationManager.getFloatParameter( "Ip Filter Ban Discard Ratio" );
//					ban_peer_discard_min_kb				= COConfigurationManager.getIntParameter( "Ip Filter Ban Discard Min KB" );
//					udp_fallback_for_failed_connection	= COConfigurationManager.getBooleanParameter( "peercontrol.udp.fallback.connect.fail" );
//					udp_fallback_for_dropped_connection	= COConfigurationManager.getBooleanParameter( "peercontrol.udp.fallback.connect.drop" );
//					udp_probe_enabled					= COConfigurationManager.getBooleanParameter( "peercontrol.udp.probe.enable" );
//					hide_a_piece						= COConfigurationManager.getBooleanParameter( "peercontrol.hide.piece" );
//
//					if ( hide_a_piece ){
//
//						disconnect_seeds_when_seeding = false;
//					}
//
//					prefer_udp_default						= COConfigurationManager.getBooleanParameter( "peercontrol.prefer.udp" );
//				}
//			});
//	}
//
//	private static IpFilter ip_filter = IpFilterManagerFactory.getSingleton().getIPFilter();
//
//	private volatile boolean	is_running 		= false;
//	private volatile boolean	is_destroyed 	= false;
//
//	private volatile ArrayList<PEPeer>	peer_transports_cow = new ArrayList<PEPeer>();	// Copy on write!
//	private final AEMonitor     peer_transports_mon	= new AEMonitor( "PEPeerControl:PT");
//
//	protected final PEPeerManagerAdapter	adapter;
//	private final DiskManager           disk_mgr;
//	private final DiskManagerPiece[]    dm_pieces;
//
//	private final PiecePicker	piecePicker;
//	private long				lastNeededUndonePieceChange;
//
//	/** literally seeding as in 100% torrent complete */
//	private boolean 			seeding_mode;
//	private boolean				restart_initiated;
//
//	private final int       _nbPieces;     //how many pieces in the torrent
//	private PEPieceImpl[]	pePieces;      //pieces that are currently in progress
//	private int				nbPiecesActive;	// how many pieces are currently in progress
//
//	private int				nbPeersSnubbed;
//
//	private PeerIdentityDataID	_hash;
//	private final byte[]        _myPeerId;
//	private PEPeerManagerStats        _stats;
//	//private final TRTrackerAnnouncer _tracker;
//	//  private int _maxUploads;
//	private int		_seeds, _peers,_remotesNoUdpNoLan;
//	private long last_remote_time;
//	private long	_timeStarted;
//	private long	_timeStartedSeeding = -1;
//	private long	_timeFinished;
//	private Average	_averageReceptionSpeed;
//
//	private long mainloop_loop_count;
//
//	private static final int MAINLOOP_ONE_SECOND_INTERVAL = 1000 / PeerControlScheduler.SCHEDULE_PERIOD_MILLIS;
//	private static final int MAINLOOP_FIVE_SECOND_INTERVAL = MAINLOOP_ONE_SECOND_INTERVAL * 5;
//	private static final int MAINLOOP_TEN_SECOND_INTERVAL = MAINLOOP_ONE_SECOND_INTERVAL * 10;
//	private static final int MAINLOOP_THIRTY_SECOND_INTERVAL = MAINLOOP_ONE_SECOND_INTERVAL * 30;
//	private static final int MAINLOOP_SIXTY_SECOND_INTERVAL = MAINLOOP_ONE_SECOND_INTERVAL * 60;
//	private static final int MAINLOOP_TEN_MINUTE_INTERVAL = MAINLOOP_SIXTY_SECOND_INTERVAL * 10;
//
//
//	private volatile ArrayList<PEPeerManagerListener> peer_manager_listeners_cow = new ArrayList<PEPeerManagerListener>();  //copy on write
//
//
//	private final List<Object[]>	piece_check_result_list     	= new ArrayList<Object[]>();
//	private final AEMonitor	piece_check_result_list_mon  	= new AEMonitor( "PEPeerControl:PCRL");
//
//	private boolean 			superSeedMode;
//	private int 				superSeedModeCurrentPiece;
//	private int 				superSeedModeNumberOfAnnounces;
//	private SuperSeedPiece[]	superSeedPieces;
//
//	private final int			hidden_piece;
//
//	private final AEMonitor     this_mon = new AEMonitor( "PEPeerControl");
//
//	private long		ip_filter_last_update_time;
//
//	private Map<Object,Object>			user_data;
//
//	private Unchoker		unchoker;
//
//	private List<Object[]>	external_rate_limiters_cow;
//
//
//	private List<PEPeer> sweepList = Collections.emptyList();
//	private int nextPEXSweepIndex = 0;
//
//
//	private final UploadHelper upload_helper = new UploadHelper() {
//		public int getPriority() {
//			return UploadHelper.PRIORITY_NORMAL;  //TODO also must call UploadSlotManager.getSingleton().updateHelper( upload_helper ); on priority change
//		}
//
//		public ArrayList<PEPeer> getAllPeers() {
//			return( peer_transports_cow );
//		}
//
//		public boolean isSeeding() {
//			return seeding_mode;
//		}
//	};
//
//
//
//	private PeerDatabase	peer_database = PeerDatabaseFactory.createPeerDatabase();
//
//	private int				bad_piece_reported		= -1;
//
//	private int				next_rescan_piece		= -1;
//	private long			rescan_piece_time		= -1;
//
//	private long			last_eta;
//	private long			last_eta_calculation;
//
//	private static final int MAX_UDP_CONNECTIONS		= 16;
//
//	private static final int PENDING_NAT_TRAVERSAL_MAX	= 32;
//	private static final int MAX_UDP_TRAVERSAL_COUNT	= 3;
//
//	private static final String	PEER_NAT_TRAVERSE_DONE_KEY	= PEPeerControlImpl.class.getName() + "::nat_trav_done";
//
//	private Map<String,PEPeerTransport>	pending_nat_traversals =
//		new LinkedHashMap<String,PEPeerTransport>(PENDING_NAT_TRAVERSAL_MAX,0.75f,true)
//	{
//		protected boolean
//		removeEldestEntry(
//			Map.Entry<String,PEPeerTransport> eldest)
//		{
//			return size() > PENDING_NAT_TRAVERSAL_MAX;
//		}
//	};
//
//	private int udp_traversal_count;
//
//	private static final int UDP_RECONNECT_MAX			= 16;
//
//	private Map<String,PEPeerTransport>	udp_reconnects =
//		new LinkedHashMap<String,PEPeerTransport>(UDP_RECONNECT_MAX,0.75f,true)
//	{
//		protected boolean
//		removeEldestEntry(
//			Map.Entry<String,PEPeerTransport> eldest)
//		{
//			return size() > UDP_RECONNECT_MAX;
//		}
//	};
//
//	private static final int UDP_RECONNECT_MIN_MILLIS	= 10*1000;
//	private long	last_udp_reconnect;
//
//	private boolean	prefer_udp;
//
//	private static final int		PREFER_UDP_BLOOM_SIZE	= 10000;
//	private volatile BloomFilter	prefer_udp_bloom;
//
//	private final LimitedRateGroup upload_limited_rate_group = new LimitedRateGroup() {
//		public String
//		getName()
//		{
//			return( "per_dl_up: " + getDisplayName());
//		}
//		public int getRateLimitBytesPerSecond() {
//			return adapter.getUploadRateLimitBytesPerSecond();
//		}
//	};
//
//	private final LimitedRateGroup download_limited_rate_group = new LimitedRateGroup() {
//		public String
//		getName()
//		{
//			return( "per_dl_down: " + getDisplayName());
//		}
//		public int getRateLimitBytesPerSecond() {
//			return adapter.getDownloadRateLimitBytesPerSecond();
//		}
//	};
//
//	private final int	partition_id;
//
//	public
//	PEPeerControlImpl(
//		byte[]					_peer_id,
//		PEPeerManagerAdapter 	_adapter,
//		DiskManager 			_diskManager,
//		int						_partition_id )
//	{
//		_myPeerId		= _peer_id;
//		adapter 		= _adapter;
//		disk_mgr 		= _diskManager;
//		partition_id	= _partition_id;
//
//		_nbPieces =disk_mgr.getNbPieces();
//		dm_pieces =disk_mgr.getPieces();
//
//		pePieces = new PEPieceImpl[_nbPieces];
//
//		hidden_piece = hide_a_piece?((int)(Math.abs(adapter.getRandomSeed())%_nbPieces)):-1;
//
//		if ( hidden_piece >= 0 ){
//
//			System.out.println( "Hidden piece for " + getDisplayName() + " = " + hidden_piece );
//		}
//
//		piecePicker = PiecePickerFactory.create( this );
//
//		COConfigurationManager.addParameterListener("Ip Filter Enabled", this);
//
//		ip_filter.addListener( this );
//
//	}
//
//
//	public void
//	start()
//	{
//		//This torrent Hash
//		try
//		{
//
//			_hash = PeerIdentityManager.createDataID( disk_mgr.getTorrent().getHash());
//
//		} catch (TOTorrentException e)
//		{
//
//			// this should never happen
//			Debug.printStackTrace( e );
//
//			_hash = PeerIdentityManager.createDataID( new byte[20] );
//		}
//
//		// the recovered active pieces
//		for (int i =0; i <_nbPieces; i++ )
//		{
//			final DiskManagerPiece dmPiece =dm_pieces[i];
//			if (!dmPiece.isDone() &&dmPiece.getNbWritten() >0)
//			{
//				addPiece(new PEPieceImpl(this, dmPiece, 0), i, true );
//			}
//		}
//
//		//The peer connections
//		peer_transports_cow = new ArrayList();
//
//		//BtManager is threaded, this variable represents the
//		// current loop iteration. It's used by some components only called
//		// at some specific times.
//		mainloop_loop_count = 0;
//
//		//The current tracker state
//		//this could be start or update
//
//		_averageReceptionSpeed = Average.getInstance(1000, 30);
//
//		// the stats
//		_stats =new PEPeerManagerStatsImpl(this);
//
//		superSeedMode = (COConfigurationManager.getBooleanParameter("Use Super Seeding") && this.getRemaining() == 0);
//
//		superSeedModeCurrentPiece = 0;
//
//		if (superSeedMode)
//		{
//			initialiseSuperSeedMode();
//		}
//
//		// initial check on finished state - future checks are driven by piece check results
//
//		// Moved out of mainLoop() so that it runs immediately, possibly changing
//		// the state to seeding.
//
//		checkFinished( true );
//
//		UploadSlotManager.getSingleton().registerHelper( upload_helper );
//
//		lastNeededUndonePieceChange =Long.MIN_VALUE;
//		_timeStarted = SystemTime.getCurrentTime();
//
//		is_running = true;
//
//		// activate after marked as running as we may synchronously add connections here due to pending activations
//
//		adapter.getPeerManagerRegistration().activate( this );
//
//		PeerNATTraverser.getSingleton().register( this );
//
//		PeerControlSchedulerFactory.getSingleton(partition_id).register(this);
//	}
//
//	public void stopAll()
//	{
//		is_running =false;
//
//		UploadSlotManager.getSingleton().deregisterHelper( upload_helper );
//
//		PeerControlSchedulerFactory.getSingleton(partition_id).unregister(this);
//
//		PeerNATTraverser.getSingleton().unregister( this );
//
//		// remove legacy controller activation
//
//		adapter.getPeerManagerRegistration().deactivate();
//
//		closeAndRemoveAllPeers("download stopped", false);
//
//		// clear pieces
//		for (int i =0; i <_nbPieces; i++ )
//		{
//			if (pePieces[i] !=null)
//				removePiece(pePieces[i], i);
//		}
//
//		// 5. Remove listeners
//		COConfigurationManager.removeParameterListener("Ip Filter Enabled", this);
//
//		ip_filter.removeListener(this);
//
//		piecePicker.destroy();
//
//		final ArrayList<PEPeerManagerListener> peer_manager_listeners = peer_manager_listeners_cow;
//
//		for( int i=0; i < peer_manager_listeners.size(); i++ ) {
//
//			((PEPeerManagerListener)peer_manager_listeners.get(i)).destroyed();
//		}
//
//		sweepList = Collections.emptyList();
//
//		pending_nat_traversals.clear();
//
//		udp_reconnects.clear();
//
//		is_destroyed = true;
//	}
//
//	public int
//	getPartitionID()
//	{
//		return( partition_id );
//	}
//
//	public boolean
//	isDestroyed()
//	{
//		return( is_destroyed );
//	}
//
//	public DiskManager getDiskManager() {  return disk_mgr;   }
//	public PiecePicker getPiecePicker()
//	{
//		return piecePicker;
//	}
//
//	public PEPeerManagerAdapter	getAdapter(){ return( adapter ); }
//
//	public String getDisplayName(){ return( adapter.getDisplayName()); }
//
//	public String
//	getName()
//	{
//		return( getDisplayName());
//	}
//
//	public void
//	schedule()
//	{
//		try {
//			updateTrackerAnnounceInterval();
//			doConnectionChecks();
//			processPieceChecks();
//
//			// note that seeding_mode -> torrent totally downloaded, not just non-dnd files
//			// complete, so there is no change of a new piece appearing done by a means such as
//			// background periodic file rescans
//
//			if ( !seeding_mode ){
//
//				checkCompletedPieces(); //check to see if we've completed anything else
//			}
//
//			checkBadPieces();
//
//			updateStats();
//
//			checkInterested();      // see if need to recheck Interested on all peers
//
//			piecePicker.updateAvailability();
//
//			checkCompletionState();	// pick up changes in completion caused by dnd file changes
//
//			checkSeeds();
//
//			if(!seeding_mode) {
//				// if we're not finished
//
//				checkRequests();
//
//				piecePicker.allocateRequests();
//
//				checkRescan();
//				checkSpeedAndReserved();
//
//				check99PercentBug();
//			}
//
//
//			updatePeersInSuperSeedMode();
//			doUnchokes();
//
//		}catch (Throwable e) {
//
//			Debug.printStackTrace( e );
//		}
//		mainloop_loop_count++;
//	}
//
//
//
//	/**
//	 * A private method that does analysis of the result sent by the tracker.
//	 * It will mainly open new connections with peers provided
//	 * and set the timeToWait variable according to the tracker response.
//	 * @param tracker_response
//	 */
//
//	private void
//	analyseTrackerResponse(
//			TRTrackerAnnouncerResponse	tracker_response )
//	{
//		// tracker_response.print();
//		final TRTrackerAnnouncerResponsePeer[]	peers = tracker_response.getPeers();
//
//		if ( peers != null ){
//			addPeersFromTracker( tracker_response.getPeers());
//		}
//
//		final Map extensions = tracker_response.getExtensions();
//
//		if (extensions != null ){
//			addExtendedPeersFromTracker( extensions );
//		}
//	}
//
//	public void
//	processTrackerResponse(
//			TRTrackerAnnouncerResponse	response )
//	{
//		// only process new peers if we're still running
//		if ( is_running ){
//			analyseTrackerResponse( response );
//		}
//	}
//
//	private void
//	addExtendedPeersFromTracker(
//			Map		extensions )
//	{
//		final Map	protocols = (Map)extensions.get("protocols");
//
//		if ( protocols != null ){
//
//			System.out.println( "PEPeerControl: tracker response contained protocol extensions");
//
//			final Iterator protocol_it = protocols.keySet().iterator();
//
//			while( protocol_it.hasNext()){
//
//				final String	protocol_name = (String)protocol_it.next();
//
//				final Map	protocol = (Map)protocols.get(protocol_name);
//
//				final List	transports = PEPeerTransportFactory.createExtendedTransports( this, protocol_name, protocol );
//
//				for (int i=0;i<transports.size();i++){
//
//					final PEPeer	transport = (PEPeer)transports.get(i);
//
//					addPeer( transport );
//				}
//			}
//		}
//	}
//
//	public List<PEPeer>
//	getPeers()
//	{
//		return( peer_transports_cow );
//	}
//
//	public List<PEPeer>
//	getPeers(
//		String	address )
//	{
//		List<PEPeer>	result = new ArrayList<PEPeer>();
//
//		Iterator<PEPeer>	it = peer_transports_cow.iterator();
//
//		while( it.hasNext()){
//
//			PEPeerTransport	peer = (PEPeerTransport)it.next();
//
//			if ( peer.getIp().equals( address )){
//
//				result.add( peer );
//			}
//		}
//
//		return( result );
//	}
//
//	public PeerDescriptor[]
//  	getPendingPeers()
//  	{
//  		return((PeerDescriptor[])peer_database.getDiscoveredPeers());
//  	}
//
//	public PeerDescriptor[]
//	getPendingPeers(
//		String	address )
//	{
//		return((PeerDescriptor[])peer_database.getDiscoveredPeers( address ));
//	}
//
//	public void
//	addPeer(
//			PEPeer		_transport )
//	{
//		if ( !( _transport instanceof PEPeerTransport )){
//
//			throw( new RuntimeException("invalid class"));
//		}
//
//		final PEPeerTransport	transport = (PEPeerTransport)_transport;
//
//		if (!ip_filter.isInRange(transport.getIp(), getDisplayName(), getTorrentHash())) {
//
//			final ArrayList peer_transports = peer_transports_cow;
//
//			if ( !peer_transports.contains(transport)){
//
//				addToPeerTransports( transport );
//
//				transport.start();
//
//			}else{
//				Debug.out( "addPeer():: peer_transports.contains(transport): SHOULD NEVER HAPPEN !" );
//				transport.closeConnection( "already connected" );
//			}
//		}else{
//
//			transport.closeConnection( "IP address blocked by filters" );
//		}
//	}
//
//	protected byte[]
//	getTorrentHash()
//	{
//		try{
//			return( disk_mgr.getTorrent().getHash());
//
//		}catch( Throwable e ){
//
//			return( null );
//		}
//	}
//	public void
//	removePeer(
//			PEPeer	_transport )
//	{
//		removePeer( _transport, "remove peer" );
//	}
//
//	public void
//	removePeer(
//			PEPeer	_transport,
//			String	reason )
//	{
//		if ( !( _transport instanceof PEPeerTransport )){
//
//			throw( new RuntimeException("invalid class"));
//		}
//
//		PEPeerTransport	transport = (PEPeerTransport)_transport;
//
//		closeAndRemovePeer( transport, reason, true );
//	}
//
//	private void closeAndRemovePeer( PEPeerTransport peer, String reason, boolean log_if_not_found )
//	{
//		boolean removed =false;
//
//		// copy-on-write semantics
//		try{
//			peer_transports_mon.enter();
//
//			if ( peer_transports_cow.contains( peer )){
//
//				final ArrayList new_peer_transports = new ArrayList( peer_transports_cow );
//
//				new_peer_transports.remove(peer);
//				peer_transports_cow =new_peer_transports;
//				removed =true;
//			}
//		}
//		finally{
//			peer_transports_mon.exit();
//		}
//
//		if( removed ) {
//			peer.closeConnection( reason );
//			peerRemoved(peer);  	//notify listeners
//		}
//		else {
//			if ( log_if_not_found ){
//				// we know this happens due to timing issues... Debug.out( "closeAndRemovePeer(): peer not removed" );
//			}
//		}
//	}
//
//	private void closeAndRemoveAllPeers( String reason, boolean reconnect ) {
//		ArrayList peer_transports;
//
//		try{
//			peer_transports_mon.enter();
//
//			peer_transports = peer_transports_cow;
//
//			peer_transports_cow = new ArrayList( 0 );
//		}
//		finally{
//			peer_transports_mon.exit();
//		}
//
//		for( int i=0; i < peer_transports.size(); i++ ) {
//			final PEPeerTransport peer = (PEPeerTransport)peer_transports.get( i );
//
//			try{
//
//				peer.closeConnection( reason );
//
//			}catch( Throwable e ){
//
//				// if something goes wrong with the close process (there's a bug in there somewhere whereby
//				// we occasionally get NPEs then we want to make sure we carry on and close the rest
//
//				Debug.printStackTrace(e);
//			}
//
//			try{
//				peerRemoved( peer );  //notify listeners
//
//			}catch( Throwable e ){
//
//				Debug.printStackTrace(e);
//			}
//		}
//
//		if( reconnect ) {
//			for( int i=0; i < peer_transports.size(); i++ ) {
//				final PEPeerTransport peer = (PEPeerTransport)peer_transports.get( i );
//
//				PEPeerTransport	reconnected_peer = peer.reconnect(false, false);
//			}
//		}
//	}
//
//
//
//
//	public void
//	addPeer(
//		String 		ip_address,
//		int			tcp_port,
//		int			udp_port,
//		boolean 	use_crypto,
//		Map			user_data )
//	{
//		final byte type = use_crypto ? PeerItemFactory.HANDSHAKE_TYPE_CRYPTO : PeerItemFactory.HANDSHAKE_TYPE_PLAIN;
//		final PeerItem peer_item = PeerItemFactory.createPeerItem( ip_address, tcp_port, PeerItem.convertSourceID( PEPeerSource.PS_PLUGIN ), type, udp_port, PeerItemFactory.CRYPTO_LEVEL_1, 0 );
//
//		byte	crypto_level = PeerItemFactory.CRYPTO_LEVEL_1;
//
//		if( !isAlreadyConnected( peer_item ) ) {
//
//			String fail_reason;
//
//			boolean	tcp_ok = TCPNetworkManager.TCP_OUTGOING_ENABLED && tcp_port > 0;
//			boolean udp_ok = UDPNetworkManager.UDP_OUTGOING_ENABLED && udp_port > 0;
//
//			if ( 	tcp_ok && !( ( prefer_udp || prefer_udp_default ) && udp_ok )){
//
//				fail_reason = makeNewOutgoingConnection( PEPeerSource.PS_PLUGIN, ip_address, tcp_port, udp_port, true, use_crypto, crypto_level, user_data );  //directly inject the the imported peer
//
//			}else if ( udp_ok ){
//
//				fail_reason = makeNewOutgoingConnection( PEPeerSource.PS_PLUGIN, ip_address, tcp_port, udp_port, false, use_crypto, crypto_level, user_data );  //directly inject the the imported peer
//
//			}else{
//
//				fail_reason = "No usable protocol";
//			}
//
//			if( fail_reason != null )  Debug.out( "Injected peer " + ip_address + ":" + tcp_port + " was not added - " + fail_reason );
//		}
//	}
//
//
//
//	private void
//	addPeersFromTracker(
//		TRTrackerAnnouncerResponsePeer[]		peers )
//	{
//
//		for (int i = 0; i < peers.length; i++){
//			final TRTrackerAnnouncerResponsePeer	peer = peers[i];
//
//			final ArrayList peer_transports = peer_transports_cow;
//
//			boolean already_connected = false;
//
//			for( int x=0; x < peer_transports.size(); x++ ) {
//				final PEPeerTransport transport = (PEPeerTransport)peer_transports.get( x );
//
//				// allow loopback connects for co-located proxy-based connections and testing
//
//				if( peer.getAddress().equals( transport.getIp() )){
//
//					final boolean same_allowed = COConfigurationManager.getBooleanParameter( "Allow Same IP Peers" ) ||
//
//					transport.getIp().equals( "127.0.0.1" );
//
//					if( !same_allowed || peer.getPort() == transport.getPort() ) {
//						already_connected = true;
//						break;
//					}
//				}
//			}
//
//			if( already_connected )  continue;
//
//			if( peer_database != null ){
//
//				byte type = peer.getProtocol() == DownloadAnnounceResultPeer.PROTOCOL_CRYPT ? PeerItemFactory.HANDSHAKE_TYPE_CRYPTO : PeerItemFactory.HANDSHAKE_TYPE_PLAIN;
//
//				byte crypto_level = peer.getAZVersion() < TRTrackerAnnouncer.AZ_TRACKER_VERSION_3?PeerItemFactory.CRYPTO_LEVEL_1:PeerItemFactory.CRYPTO_LEVEL_2;
//
//				PeerItem item = PeerItemFactory.createPeerItem(
//						peer.getAddress(),
//						peer.getPort(),
//						PeerItem.convertSourceID( peer.getSource() ),
//						type,
//						peer.getUDPPort(),
//						crypto_level,
//						peer.getUploadSpeed());
//
//				peerDiscovered( null, item );
//
//				peer_database.addDiscoveredPeer( item );
//			}
//
//			int	http_port = peer.getHTTPPort();
//
//			if ( http_port != 0 ){
//
//				adapter.addHTTPSeed( peer.getAddress(), http_port );
//			}
//		}
//	}
//
//
//	/**
//	 * Request a new outgoing peer connection.
//	 * @param address ip of remote peer
//	 * @param port remote peer listen port
//	 * @return null if the connection was added to the transport list, reason if rejected
//	 */
//
//	private String
//	makeNewOutgoingConnection(
//			String	peer_source,
//			String 	address,
//			int 		tcp_port,
//			int			udp_port,
//			boolean		use_tcp,
//			boolean 	require_crypto,
//			byte		crypto_level,
//			Map			user_data )
//	{
//		//make sure this connection isn't filtered
//
//		if( ip_filter.isInRange( address, getDisplayName(), getTorrentHash())) {
//			return "IPFilter block";
//		}
//
//		//make sure we need a new connection
//		final int needed = getMaxNewConnectionsAllowed();
//
//		boolean	is_priority_connection = false;
//
//		if ( user_data != null ){
//
//			Boolean pc = (Boolean)user_data.get( Peer.PR_PRIORITY_CONNECTION );
//
//			if ( pc != null && pc.booleanValue()){
//
//				is_priority_connection = true;
//			}
//		}
//
//		if( needed == 0 ){
//
//			if ( 	peer_source != PEPeerSource.PS_PLUGIN ||
//					!doOptimisticDisconnect(
//							AddressUtils.isLANLocalAddress( address ) != AddressUtils.LAN_LOCAL_NO,
//							is_priority_connection )){
//
//				return "Too many connections";
//			}
//		}
//
//		//make sure not already connected to the same IP address; allow loopback connects for co-located proxy-based connections and testing
//		final boolean same_allowed = COConfigurationManager.getBooleanParameter( "Allow Same IP Peers" ) || address.equals( "127.0.0.1" );
//		if( !same_allowed && PeerIdentityManager.containsIPAddress( _hash, address ) ){
//			return "Already connected to IP";
//		}
//
//		if( PeerUtils.ignorePeerPort( tcp_port ) ) {
//			if (Logger.isEnabled())
//				Logger.log(new LogEvent(disk_mgr.getTorrent(), LOGID,
//						"Skipping connect with " + address + ":" + tcp_port
//						+ " as peer port is in ignore list."));
//			return "TCP port in ignore list";
//		}
//
//			//start the connection
//
//		PEPeerTransport real = PEPeerTransportFactory.createTransport( this, peer_source, address, tcp_port, udp_port, use_tcp, require_crypto, crypto_level, user_data );
//
//		addToPeerTransports( real );
//
//		return null;
//	}
//
//
//	/**
//	 * A private method that checks if PEPieces being downloaded are finished
//	 * If all blocks from a PEPiece are written to disk, this method will
//	 * queue the piece for hash check.
//	 * Elsewhere, if it passes sha-1 check, it will be marked as downloaded,
//	 * otherwise, it will unmark it as fully downloaded, so blocks can be retreived again.
//	 */
//	private void checkCompletedPieces() {
//		if ((mainloop_loop_count %MAINLOOP_ONE_SECOND_INTERVAL) !=0)
//			return;
//
//		//for every piece
//		for (int i = 0; i <_nbPieces; i++) {
//			final DiskManagerPiece dmPiece =dm_pieces[i];
//			//if piece is completly written, not already checking, and not Done
//			if (dmPiece.isNeedsCheck())
//			{
//				//check the piece from the disk
//				dmPiece.setChecking();
//
//				DiskManagerCheckRequest req =
//					disk_mgr.createCheckRequest(
//							i, new Integer(CHECK_REASON_DOWNLOADED));
//
//				req.setAdHoc( false );
//
//				disk_mgr.enqueueCheckRequest(  req, this );
//			}
//		}
//	}
//
//	/** Checks given piece to see if it's active but empty, and if so deactivates it.
//	 * @param pieceNumber to check
//	 * @return true if the piece was removed and is no longer active (pePiece ==null)
//	 */
//	private boolean
//	checkEmptyPiece(
//		final int pieceNumber )
//	{
//        if ( piecePicker.isInEndGameMode()){
//
//			return false;   // be sure to not remove pieces in EGM
//        }
//
//		final PEPiece pePiece =pePieces[pieceNumber];
//		final DiskManagerPiece dmPiece =dm_pieces[pieceNumber];
//
//		if ( pePiece == null || pePiece.isRequested())
//			return false;
//
//		if (dmPiece.getNbWritten() >0 ||pePiece.getNbUnrequested() < pePiece.getNbBlocks() ||pePiece.getReservedBy() !=null)
//			return false;
//
//			// reset in case dmpiece is in some skanky state
//
//		pePiece.reset();
//
//		removePiece(pePiece, pieceNumber);
//		return true;
//	}
//
//	/**
//	 * Check if a piece's Speed is too fast for it to be getting new data
//	 * and if a reserved pieced failed to get data within 120 seconds
//	 */
//	private void checkSpeedAndReserved()
//	{
//		// only check every 5 seconds
//		if(mainloop_loop_count % MAINLOOP_FIVE_SECOND_INTERVAL != 0)
//			return;
//
//		final int				nbPieces	=_nbPieces;
//		final PEPieceImpl[] pieces =pePieces;
//		//for every piece
//		for (int i =0; i <nbPieces; i++)
//		{
//			// placed before null-check in case it really removes a piece
//			checkEmptyPiece(i);
//
//
//			final PEPieceImpl pePiece =pieces[i];
//			// these checks are only against pieces being downloaded
//			// yet needing requests still/again
//			if (pePiece !=null)
//			{
//				final long timeSinceActivity =pePiece.getTimeSinceLastActivity()/1000;
//
//				int pieceSpeed =pePiece.getSpeed();
//				// block write speed slower than piece speed
//				if (pieceSpeed > 0 && timeSinceActivity*pieceSpeed*0.25 > DiskManager.BLOCK_SIZE/1024)
//				{
//					if(pePiece.getNbUnrequested() > 2)
//						pePiece.setSpeed(pieceSpeed-1);
//					else
//						pePiece.setSpeed(0);
//				}
//
//
//				if(timeSinceActivity > 120)
//				{
//					pePiece.setSpeed(0);
//					// has reserved piece gone stagnant?
//					final String reservingPeer =pePiece.getReservedBy();
//					if(reservingPeer !=null)
//					{
//						final PEPeerTransport pt = getTransportFromAddress(reservingPeer);
//						// Peer is too slow; Ban them and unallocate the piece
//						// but, banning is no good for peer types that get pieces reserved
//						// to them for other reasons, such as special seed peers
//						if (needsMD5CheckOnCompletion(i))
//							badPeerDetected(reservingPeer, i);
//						else if (pt != null)
//							closeAndRemovePeer(pt, "Reserved piece data timeout; 120 seconds", true);
//
//						pePiece.setReservedBy(null);
//					}
//
//					if (!piecePicker.isInEndGameMode()){
//						pePiece.checkRequests();
//					}
//
//					checkEmptyPiece(i);
//				}
//
//			}
//		}
//	}
//
//	private void
//	check99PercentBug()
//	{
//		// there's a bug whereby pieces are left downloaded but never written. might have been fixed by
//		// changes to the "write result" logic, however as a stop gap I'm adding code to scan for such
//		// stuck pieces and reset them
//
//		if ( mainloop_loop_count % MAINLOOP_SIXTY_SECOND_INTERVAL == 0 ) {
//
//			long	now = SystemTime.getCurrentTime();
//
//			for ( int i=0;i<pePieces.length;i++){
//
//				PEPiece	pe_piece = pePieces[ i ];
//
//				if ( pe_piece != null ){
//
//					DiskManagerPiece	dm_piece = dm_pieces[i];
//
//					if ( !dm_piece.isDone()){
//
//						if ( pe_piece.isDownloaded()){
//
//							if ( now - pe_piece.getLastDownloadTime(now) > stalled_piece_timeout ){
//
//								// people with *very* slow disk writes can trigger this (I've been talking to a user
//								// with a SAN that has .5 second write latencies when checking a file at the same time
//								// this means that when dowloading > 32K/sec things start backing up). Eventually the
//								// write controller will start blocking the network thread to prevent unlimited
//								// queueing but until that time we need to handle this situation slightly better)
//
//								// if there are any outstanding requests for this piece then leave it alone
//
//								if ( !( disk_mgr.hasOutstandingWriteRequestForPiece( i ) ||
//										disk_mgr.hasOutstandingReadRequestForPiece( i ) ||
//										disk_mgr.hasOutstandingCheckRequestForPiece( i ) )){
//
//									Debug.out( "Fully downloaded piece stalled pending write, resetting p_piece " + i );
//
//									pe_piece.reset();
//								}
//							}
//						}
//					}
//				}
//
//			}
//		}
//	}
//
//	private void checkInterested()
//	{
//		if ( (mainloop_loop_count %MAINLOOP_ONE_SECOND_INTERVAL) != 0 ){
//			return;
//		}
//
//		if (lastNeededUndonePieceChange >=piecePicker.getNeededUndonePieceChange())
//			return;
//
//		lastNeededUndonePieceChange =piecePicker.getNeededUndonePieceChange();
//
//		final ArrayList peer_transports = peer_transports_cow;
//		int cntPeersSnubbed =0;	// recount # snubbed peers while we're at it
//		for (int i =0; i <peer_transports.size(); i++)
//		{
//			final PEPeerTransport peer =(PEPeerTransport)peer_transports.get(i);
//			peer.checkInterested();
//			if (peer.isSnubbed())
//				cntPeersSnubbed++;
//		}
//		setNbPeersSnubbed(cntPeersSnubbed);
//	}
//
//
//	/**
//	 * Private method to process the results given by DiskManager's
//	 * piece checking thread via asyncPieceChecked(..)
//	 */
//	private void
//	processPieceChecks()
//	{
//		if ( piece_check_result_list.size() > 0 ){
//
//			final List pieces;
//
//			// process complete piece results
//
//			try{
//				piece_check_result_list_mon.enter();
//
//				pieces = new ArrayList( piece_check_result_list );
//
//				piece_check_result_list.clear();
//
//			}finally{
//
//				piece_check_result_list_mon.exit();
//			}
//
//			final Iterator it = pieces.iterator();
//
//			while (it.hasNext()) {
//
//				final Object[]	data = (Object[])it.next();
//
//				processPieceCheckResult((DiskManagerCheckRequest)data[0],((Integer)data[1]).intValue());
//
//			}
//		}
//	}
//
//	private void
//	checkBadPieces()
//	{
//		if ( mainloop_loop_count % MAINLOOP_SIXTY_SECOND_INTERVAL == 0 ){
//
//			if ( bad_piece_reported != -1 ){
//
//				DiskManagerCheckRequest	req =
//					disk_mgr.createCheckRequest(
//						bad_piece_reported,
//						new Integer( CHECK_REASON_BAD_PIECE_CHECK ));
//
//				req.setLowPriority( true );
//
//			   	if ( Logger.isEnabled()){
//
//					Logger.log(
//							new LogEvent(
//								disk_mgr.getTorrent(), LOGID,
//								"Rescanning reported-bad piece " + bad_piece_reported ));
//
//			   	}
//
//				bad_piece_reported	= -1;
//
//				try{
//					disk_mgr.enqueueCheckRequest( req, this );
//
//				}catch( Throwable e ){
//
//
//					Debug.printStackTrace(e);
//				}
//			}
//		}
//	}
//
//	private void
//	checkRescan()
//	{
//		if ( rescan_piece_time == 0 ){
//
//			// pending a piece completion
//
//			return;
//		}
//
//		if ( next_rescan_piece == -1 ){
//
//			if ( mainloop_loop_count % MAINLOOP_FIVE_SECOND_INTERVAL == 0 ){
//
//				if ( adapter.isPeriodicRescanEnabled()){
//
//					next_rescan_piece	= 0;
//				}
//			}
//		}else{
//
//			if ( mainloop_loop_count % MAINLOOP_TEN_MINUTE_INTERVAL == 0 ){
//
//				if ( !adapter.isPeriodicRescanEnabled()){
//
//					next_rescan_piece	= -1;
//				}
//			}
//		}
//
//		if ( next_rescan_piece == -1 ){
//
//			return;
//		}
//
//		// delay as required
//
//		final long	now = SystemTime.getCurrentTime();
//
//		if ( rescan_piece_time > now ){
//
//			rescan_piece_time	= now;
//		}
//
//		// 250K/sec limit
//
//		final long	piece_size = disk_mgr.getPieceLength();
//
//		final long	millis_per_piece = piece_size / 250;
//
//		if ( now - rescan_piece_time < millis_per_piece ){
//
//			return;
//		}
//
//		while( next_rescan_piece != -1 ){
//
//			int	this_piece = next_rescan_piece;
//
//			next_rescan_piece++;
//
//			if ( next_rescan_piece == _nbPieces ){
//
//				next_rescan_piece	= -1;
//			}
//
//				// this functionality is to pick up pieces that have been downloaded OUTSIDE of
//				// Azureus - e.g. when two torrents are sharing a single file. Hence the check on
//				// the piece NOT being done
//
//			if ( pePieces[this_piece] == null && !dm_pieces[this_piece].isDone() && dm_pieces[this_piece].isNeeded()){
//
//				DiskManagerCheckRequest	req =
//					disk_mgr.createCheckRequest(
//							this_piece,
//							new Integer( CHECK_REASON_SCAN ));
//
//				req.setLowPriority( true );
//
//				if ( Logger.isEnabled()){
//
//					Logger.log(
//							new LogEvent(
//									disk_mgr.getTorrent(), LOGID,
//									"Rescanning piece " + this_piece ));
//
//				}
//
//				rescan_piece_time	= 0;	// mark as check piece in process
//
//				try{
//					disk_mgr.enqueueCheckRequest( req, this );
//
//				}catch( Throwable e ){
//
//					rescan_piece_time	= now;
//
//					Debug.printStackTrace(e);
//				}
//
//				break;
//			}
//		}
//	}
//
//	public void
//	badPieceReported(
//		PEPeerTransport		originator,
//		int					piece_number )
//	{
//		Debug.outNoStack( getDisplayName() + ": bad piece #" + piece_number + " reported by " + originator.getIp());
//
//		if ( piece_number < 0 || piece_number >= _nbPieces ){
//
//			return;
//		}
//
//		bad_piece_reported = piece_number;
//	}
//
//	/**
//	 * This method checks if the downloading process is finished.
//	 *
//	 */
//	private void
//	checkFinished(
//			boolean start_of_day )
//	{
//		final boolean all_pieces_done =disk_mgr.getRemainingExcludingDND() ==0;
//
//		if (all_pieces_done)
//		{
//			seeding_mode	= true;
//
//			prefer_udp_bloom = null;
//
//			piecePicker.clearEndGameChunks();
//
//			if (!start_of_day)
//				adapter.setStateFinishing();
//
//			_timeFinished = SystemTime.getCurrentTime();
//			final ArrayList peer_transports = peer_transports_cow;
//
//			//remove previous snubbing
//			for (int i =0; i <peer_transports.size(); i++ )
//			{
//				final PEPeerTransport pc = (PEPeerTransport) peer_transports.get(i);
//				pc.setSnubbed(false);
//			}
//			setNbPeersSnubbed(0);
//
//			final boolean checkPieces =COConfigurationManager.getBooleanParameter( "Check Pieces on Completion" );
//
//			//re-check all pieces to make sure they are not corrupt, but only if we weren't already complete
//			if (checkPieces &&!start_of_day)
//			{
//				final DiskManagerCheckRequest req =disk_mgr.createCheckRequest(-1, new Integer(CHECK_REASON_COMPLETE));
//				disk_mgr.enqueueCompleteRecheckRequest(req, this);
//			}
//
//			_timeStartedSeeding = SystemTime.getCurrentTime();
//
//			try{
//				disk_mgr.saveResumeData(false);
//
//			}catch( Throwable e ){
//				Debug.out( "Failed to save resume data", e );
//			}
//			adapter.setStateSeeding( start_of_day );
//			disk_mgr.downloadEnded();
//		} else
//		{
//			seeding_mode = false;
//		}
//	}
//
//	protected void
//	checkCompletionState()
//	{
//		if ( mainloop_loop_count % MAINLOOP_ONE_SECOND_INTERVAL != 0 ){
//
//			return;
//		}
//
//		boolean dm_done = disk_mgr.getRemainingExcludingDND() == 0;
//
//		if ( seeding_mode ){
//
//			if ( !dm_done ){
//
//				seeding_mode = false;
//
//				_timeStartedSeeding = -1;
//				_timeFinished		= 0;
//
//				Logger.log(
//						new LogEvent(	disk_mgr.getTorrent(), LOGID,
//								"Turning off seeding mode for PEPeerManager"));
//			}
//		}else{
//
//			if ( dm_done ){
//
//				checkFinished( false );
//
//				if ( seeding_mode ){
//
//					Logger.log(
//							new LogEvent(	disk_mgr.getTorrent(), LOGID,
//									"Turning on seeding mode for PEPeerManager"));
//				}
//			}
//		}
//	}
//
//	/**
//	 * This method will locate expired requests on peers, will cancel them,
//	 * and mark the peer as snubbed if we haven't received usefull data from
//	 * them within the last 60 seconds
//	 */
//	private void checkRequests()
//	{
//		// to be honest I don't see why this can't be 5 seconds, but I'm trying 1 second
//		// now as the existing 0.1 second is crazy given we're checking for events that occur
//		// at 60+ second intervals
//
//		if ( mainloop_loop_count % MAINLOOP_ONE_SECOND_INTERVAL != 0 ){
//
//			return;
//		}
//
//		final long now =SystemTime.getCurrentTime();
//
//		//for every connection
//		final ArrayList peer_transports = peer_transports_cow;
//		for (int i =peer_transports.size() -1; i >=0 ; i--)
//		{
//			final PEPeerTransport pc =(PEPeerTransport)peer_transports.get(i);
//			if (pc.getPeerState() ==PEPeer.TRANSFERING)
//			{
//				final List expired = pc.getExpiredRequests();
//				if (expired !=null &&expired.size() >0)
//				{   // now we know there's a request that's > 60 seconds old
//					final boolean isSeed =pc.isSeed();
//					// snub peers that haven't sent any good data for a minute
//					final long timeSinceGoodData =pc.getTimeSinceGoodDataReceived();
//					if (timeSinceGoodData <0 ||timeSinceGoodData >60 *1000)
//						pc.setSnubbed(true);
//
//					//Only cancel first request if more than 2 mins have passed
//					DiskManagerReadRequest request =(DiskManagerReadRequest) expired.get(0);
//
//					final long timeSinceData =pc.getTimeSinceLastDataMessageReceived();
//					final boolean noData =(timeSinceData <0) ||timeSinceData >(1000 *(isSeed ?120 :60));
//					final long timeSinceOldestRequest = now - request.getTimeCreated(now);
//
//
//					//for every expired request
//					for (int j = (timeSinceOldestRequest >120 *1000 && noData)  ? 0 : 1; j < expired.size(); j++)
//					{
//						//get the request object
//						request =(DiskManagerReadRequest) expired.get(j);
//						//Only cancel first request if more than 2 mins have passed
//						pc.sendCancel(request);				//cancel the request object
//						//get the piece number
//						final int pieceNumber = request.getPieceNumber();
//						PEPiece	pe_piece = pePieces[pieceNumber];
//						//unmark the request on the block
//						if ( pe_piece != null )
//							pe_piece.clearRequested(request.getOffset() /DiskManager.BLOCK_SIZE);
//						// remove piece if empty so peers can choose something else, except in end game
//						if (!piecePicker.isInEndGameMode())
//							checkEmptyPiece(pieceNumber);
//					}
//				}
//			}
//		}
//	}
//
//	private void
//	updateTrackerAnnounceInterval()
//	{
//		if ( mainloop_loop_count % MAINLOOP_FIVE_SECOND_INTERVAL != 0 ){
//			return;
//		}
//
//		final int WANT_LIMIT = 100;
//
//		int num_wanted = getMaxNewConnectionsAllowed();
//
//		final boolean has_remote = adapter.isNATHealthy();
//		if( has_remote ) {
//			//is not firewalled, so can accept incoming connections,
//			//which means no need to continually keep asking the tracker for peers
//			num_wanted = (int)(num_wanted / 1.5);
//		}
//
//		if ( num_wanted < 0 || num_wanted > WANT_LIMIT ) {
//			num_wanted = WANT_LIMIT;
//		}
//
//		int current_connection_count = PeerIdentityManager.getIdentityCount( _hash );
//
//		final TRTrackerScraperResponse tsr = adapter.getTrackerScrapeResponse();
//
//		if( tsr != null && tsr.isValid() ) {  //we've got valid scrape info
//			final int num_seeds = tsr.getSeeds();
//			final int num_peers = tsr.getPeers();
//
//			final int swarm_size;
//
//			if( seeding_mode ) {
//				//Only use peer count when seeding, as other seeds are unconnectable.
//				//Since trackers return peers randomly (some of which will be seeds),
//				//backoff by the seed2peer ratio since we're given only that many peers
//				//on average each announce.
//				final float ratio = (float)num_peers / (num_seeds + num_peers);
//				swarm_size = (int)(num_peers * ratio);
//			}
//			else {
//				swarm_size = num_peers + num_seeds;
//			}
//
//			if( swarm_size < num_wanted ) {  //lower limit to swarm size if necessary
//				num_wanted = swarm_size;
//			}
//		}
//
//		if( num_wanted < 1 ) {  //we dont need any more connections
//			adapter.setTrackerRefreshDelayOverrides( 100 );  //use normal announce interval
//			return;
//		}
//
//		if( current_connection_count == 0 )  current_connection_count = 1;  //fudge it :)
//
//		final int current_percent = (current_connection_count * 100) / (current_connection_count + num_wanted);
//
//		adapter.setTrackerRefreshDelayOverrides( current_percent );  //set dynamic interval override
//	}
//
//	public boolean
//	hasDownloadablePiece()
//	{
//		return( piecePicker.hasDownloadablePiece());
//	}
//
//	public int[] getAvailability()
//	{
//		return piecePicker.getAvailability();
//	}
//
//	//this only gets called when the My Torrents view is displayed
//	public float getMinAvailability()
//	{
//		return piecePicker.getMinAvailability();
//	}
//
//	public float getAvgAvail()
//	{
//		return piecePicker.getAvgAvail();
//	}
//
//	public long getAvailWentBadTime()
//	{
//		return( piecePicker.getAvailWentBadTime());
//	}
//
//	public void addPeerTransport( PEPeerTransport transport ) {
//    if (!ip_filter.isInRange(transport.getIp(), getDisplayName(), getTorrentHash())) {
//			final ArrayList peer_transports = peer_transports_cow;
//
//			if (!peer_transports.contains( transport )) {
//				addToPeerTransports(transport);
//			}
//			else{
//				Debug.out( "addPeerTransport():: peer_transports.contains(transport): SHOULD NEVER HAPPEN !" );
//				transport.closeConnection( "already connected" );
//			}
//		}
//		else {
//			transport.closeConnection( "IP address blocked by filters" );
//		}
//	}
//
//
//	/**
//	 * Do all peer choke/unchoke processing.
//	 */
//	private void doUnchokes() {
//
//		// logic below is either 1 second or 10 secondly, bail out early id neither
//
//		if( !UploadSlotManager.AUTO_SLOT_ENABLE ) {		 //manual per-torrent unchoke slot mode
//
//			if( mainloop_loop_count % MAINLOOP_ONE_SECOND_INTERVAL != 0 ) {
//				return;
//			}
//
//			final int max_to_unchoke = adapter.getMaxUploads();  //how many simultaneous uploads we should consider
//			final ArrayList peer_transports = peer_transports_cow;
//
//			//determine proper unchoker
//			if( seeding_mode ) {
//				if( unchoker == null || !(unchoker.isSeedingUnchoker()) ) {
//					unchoker = UnchokerFactory.getSingleton().getUnchoker( true );
//				}
//			}
//			else {
//				if( unchoker == null || unchoker.isSeedingUnchoker()) {
//					unchoker = UnchokerFactory.getSingleton().getUnchoker( false );
//				}
//			}
//
//				//do main choke/unchoke update every 10 secs
//
//			if ( mainloop_loop_count % MAINLOOP_TEN_SECOND_INTERVAL == 0 ){
//
//				final boolean refresh = mainloop_loop_count % MAINLOOP_THIRTY_SECOND_INTERVAL == 0;
//
//				unchoker.calculateUnchokes( max_to_unchoke, peer_transports, refresh, adapter.hasPriorityConnection());
//
//				ArrayList	chokes 		= unchoker.getChokes();
//				ArrayList	unchokes	= unchoker.getUnchokes();
//
//				addFastUnchokes( unchokes );
//
//				UnchokerUtil.performChokes( chokes, unchokes );
//
//			}else if ( mainloop_loop_count % MAINLOOP_ONE_SECOND_INTERVAL == 0 ) {  //do quick unchoke check every 1 sec
//
//				ArrayList unchokes = unchoker.getImmediateUnchokes( max_to_unchoke, peer_transports );
//
//				addFastUnchokes( unchokes );
//
//				UnchokerUtil.performChokes( null, unchokes );
//			}
//		}
//	}
//
//	private void
//	addFastUnchokes(
//		ArrayList	peers_to_unchoke )
//	{
//		for( Iterator it=peer_transports_cow.iterator();it.hasNext();) {
//
//			PEPeerTransport peer = (PEPeerTransport)it.next();
//
//			if ( 	peer.getConnectionState() != PEPeerTransport.CONNECTION_FULLY_ESTABLISHED ||
//					!UnchokerUtil.isUnchokable( peer, true ) ||
//					peers_to_unchoke.contains( peer )){
//
//				continue;
//			}
//
//			if( peer.isLANLocal()){
//
//				peers_to_unchoke.add( peer );
//
//			}else if ( 	fast_unchoke_new_peers &&
//						peer.getData( "fast_unchoke_done" ) == null ){
//
//				peer.setData( "fast_unchoke_done", "" );
//
//				peers_to_unchoke.add( peer );
//			}
//		}
//	}
//
////	send the have requests out
//	private void sendHave(int pieceNumber) {
//		//fo
//		final ArrayList peer_transports = peer_transports_cow;
//
//		for (int i = 0; i < peer_transports.size(); i++) {
//			//get a peer connection
//			final PEPeerTransport pc = (PEPeerTransport) peer_transports.get(i);
//			//send the have message
//			pc.sendHave(pieceNumber);
//		}
//
//	}
//
//	// Method that checks if we are connected to another seed, and if so, disconnect from him.
//	private void checkSeeds() {
//		//proceed on mainloop 1 second intervals if we're a seed and we want to force disconnects
//		if ((mainloop_loop_count % MAINLOOP_ONE_SECOND_INTERVAL) != 0)
//			return;
//
//		if (!disconnect_seeds_when_seeding ){
//			return;
//		}
//
//		ArrayList to_close = null;
//
//		final ArrayList peer_transports = peer_transports_cow;
//		for (int i = 0; i < peer_transports.size(); i++) {
//			final PEPeerTransport pc = (PEPeerTransport) peer_transports.get(i);
//
//			if (pc != null && pc.getPeerState() == PEPeer.TRANSFERING && ((isSeeding() && pc.isSeed()) || pc.isRelativeSeed())) {
//				if( to_close == null )  to_close = new ArrayList();
//				to_close.add( pc );
//			}
//		}
//
//		if( to_close != null ) {
//			for( int i=0; i < to_close.size(); i++ ) {
//				closeAndRemovePeer( (PEPeerTransport)to_close.get(i), "disconnect other seed when seeding", false );
//			}
//		}
//	}
//
//
//
//
//	private void updateStats() {
//
//		if ( (mainloop_loop_count %MAINLOOP_ONE_SECOND_INTERVAL) != 0 ){
//			return;
//		}
//
//		//calculate seeds vs peers
//		final ArrayList peer_transports = peer_transports_cow;
//
//		int	new_seeds = 0;
//		int new_peers = 0;
//		int newTcpRemotes = 0;
//
//		for (Iterator it=peer_transports.iterator();it.hasNext();){
//			final PEPeerTransport pc = (PEPeerTransport) it.next();
//			if (pc.getPeerState() == PEPeer.TRANSFERING) {
//				if (pc.isSeed())
//					new_seeds++;
//				else
//					new_peers++;
//
//				if(pc.isIncoming() && pc.isTCP() && !pc.isLANLocal()) {
//					newTcpRemotes++;
//				}
//			}
//		}
//
//		_seeds = new_seeds;
//		_peers = new_peers;
//		_remotesNoUdpNoLan = newTcpRemotes;
//	}
//	/**
//	 * The way to unmark a request as being downloaded, or also
//	 * called by Peer connections objects when connection is closed or choked
//	 * @param request a DiskManagerReadRequest holding details of what was canceled
//	 */
//	public void requestCanceled(DiskManagerReadRequest request)
//	{
//		final int pieceNumber =request.getPieceNumber();  //get the piece number
//		PEPiece pe_piece = pePieces[pieceNumber];
//		if (pe_piece != null )
//			pe_piece.clearRequested(request.getOffset() /DiskManager.BLOCK_SIZE);
//	}
//
//
//	public PEPeerControl
//	getControl()
//	{
//		return( this );
//	}
//
//	public byte[][]
//	              getSecrets(
//	            		  int	crypto_level )
//	{
//		return( adapter.getSecrets( crypto_level ));
//	}
//
////	get the hash value
//	public byte[] getHash() {
//		return _hash.getDataID();
//	}
//
//	public PeerIdentityDataID
//	getPeerIdentityDataID()
//	{
//		return( _hash );
//	}
//
////	get the peer id value
//	public byte[] getPeerId() {
//		return _myPeerId;
//	}
//
////	get the remaining percentage
//	public long getRemaining() {
//		return disk_mgr.getRemaining();
//	}
//
//
//	public void discarded(PEPeer peer, int length) {
//		if (length > 0){
//			_stats.discarded(peer, length);
//
//				// discards are more likely during end-game-mode
//
//			if ( ban_peer_discard_ratio > 0 && !( piecePicker.isInEndGameMode() || piecePicker.hasEndGameModeBeenAbandoned())){
//
//				long	received 	= peer.getStats().getTotalDataBytesReceived();
//				long	discarded	= peer.getStats().getTotalBytesDiscarded();
//
//				long	non_discarded = received - discarded;
//
//				if ( non_discarded < 0 ){
//
//					non_discarded = 0;
//				}
//
//				if ( discarded >= ban_peer_discard_min_kb * 1024 ){
//
//					if ( 	non_discarded == 0 ||
//							((float)discarded) / non_discarded >= ban_peer_discard_ratio ){
//
//						badPeerDetected( peer.getIp(), -1 );
//					}
//				}
//			}
//		}
//	}
//
//	public void dataBytesReceived(PEPeer peer, int length) {
//		if (length > 0) {
//			_stats.dataBytesReceived(peer,length);
//
//			_averageReceptionSpeed.addValue(length);
//		}
//	}
//
//
//	public void protocolBytesReceived(PEPeer peer,  int length ) {
//		if (length > 0) {
//			_stats.protocolBytesReceived(peer,length);
//		}
//	}
//
//	public void dataBytesSent(PEPeer peer, int length) {
//		if (length > 0) {
//			_stats.dataBytesSent(peer, length );
//		}
//	}
//
//
//	public void protocolBytesSent( PEPeer peer, int length ) {
//		if (length > 0) {
//			_stats.protocolBytesSent(peer,length);
//		}
//	}
//
//	/** DiskManagerWriteRequestListener message
//	 * @see org.gudy.azureus2.core3.disk.DiskManagerWriteRequestListener
//	 */
//	public void writeCompleted(DiskManagerWriteRequest request)
//	{
//		final int pieceNumber =request.getPieceNumber();
//
//		DiskManagerPiece	dm_piece = dm_pieces[pieceNumber];
//
//		if (!dm_piece.isDone()){
//
//			final PEPiece pePiece =pePieces[pieceNumber];
//
//			if (pePiece !=null){
//
//				pePiece.setWritten((PEPeer)request.getUserData(), request.getOffset() /DiskManager.BLOCK_SIZE );
//
//			}else{
//
//				// this is a way of fixing a 99.9% bug where a dmpiece is left in a
//				// fully downloaded state with the underlying pe_piece null. Possible explanation is
//				// that a slow peer sends an entire piece at around the time a pe_piece gets reset
//				// due to inactivity.
//
//				// we also get here when recovering data that has come in late after the piece has
//				// been abandoned
//
//				dm_piece.setWritten( request.getOffset() /DiskManager.BLOCK_SIZE );
//			}
//		}
//	}
//
//	public void
//	writeFailed(
//			DiskManagerWriteRequest 	request,
//			Throwable		 			cause )
//	{
//		// if the write has failed then the download will have been stopped so there is no need to try
//		// and reset the piece
//	}
//
//
//		/**
//		 *
//		 * @param data_offset	MUST be block aligned within the TORRENT
//		 * @param data
//		 * @return		buffers not yet written
//		 * @throws Exception
//		 */
//
//	public DirectByteBuffer[]
//	write(
//		long					data_offset,
//		DirectByteBuffer[]		data )
//
//		throws Exception
//	{
//		if ( data_offset % DiskManager.BLOCK_SIZE != 0 ){
//
//			throw( new Exception( "data must start at a block offset" ));
//		}
//
//		int	piece_length = disk_mgr.getPieceLength();
//
//		int	data_length = 0;
//
//		for( DirectByteBuffer buffer: data ){
//
//			data_length += buffer.remaining( DirectByteBuffer.SS_DW );
//		}
//
//		long	written = 0;
//
//		int		buffer_number = 0;
//
//		for ( int i=0;i<data_length;i+=DiskManager.BLOCK_SIZE ){
//
//			int	rem = data_length - i;
//
//			if ( rem > DiskManager.BLOCK_SIZE ){
//
//				rem = DiskManager.BLOCK_SIZE;
//			}
//
//			int	piece_number = (int)( data_offset / piece_length );
//
//			DiskManagerPiece dm_piece = dm_pieces[piece_number];
//
//			int	block_number = (int)(data_offset - ( piece_number * piece_length )) / DiskManager.BLOCK_SIZE;
//
//			int block_size = dm_piece.getBlockSize( block_number );
//
//			if ( rem < block_size ){
//
//				break;
//			}
//
//			DirectByteBuffer chunk = DirectByteBufferPool.getBuffer( DirectByteBuffer.AL_BT_PIECE, block_size );
//
//
//			writeBlock( piece_number, block_number*DiskManager.BLOCK_SIZE, chunk, null, true );
//
//			written 		+= rem;
//			data_offset		+= rem;
//		}
//
//		List<DirectByteBuffer>	unwritten = new ArrayList<DirectByteBuffer>();
//
//		for (int i=buffer_number;i<data.length;i++){
//
//			if (data[i].hasRemaining( DirectByteBuffer.SS_DW )){
//
//				unwritten.add( data[i] );
//
//			}else{
//
//				data[i].returnToPool();
//			}
//		}
//
//		return( unwritten.toArray( new DirectByteBuffer[ unwritten.size()] ));
//	}
//
//
//	/** This method will queue up a dism manager write request for the block if the block is not already written.
//	 * It will send out cancels for the block to all peer either if in end-game mode, or per cancel param
//	 * @param pieceNumber to potentialy write to
//	 * @param offset within piece to queue write for
//	 * @param data to be writen
//	 * @param sender peer that sent this data
//	 * @param cancel if cancels definatly need to be sent to all peers for this request
//	 */
//	public void writeBlock(int pieceNumber, int offset, DirectByteBuffer data, PEPeer sender, boolean cancel)
//	{
//		final int blockNumber =offset /DiskManager.BLOCK_SIZE;
//		final DiskManagerPiece dmPiece =dm_pieces[pieceNumber];
//		if (dmPiece.isWritten(blockNumber))
//		{
//			data.returnToPool();
//			return;
//		}
//
//		PEPiece	pe_piece = pePieces[ pieceNumber ];
//
//		if ( pe_piece != null ){
//
//			pe_piece.setDownloaded( offset );
//		}
//
//		final DiskManagerWriteRequest request =disk_mgr.createWriteRequest(pieceNumber, offset, data, sender);
//		disk_mgr.enqueueWriteRequest(request, this );
//		// In case we are in endGame mode, remove the block from the chunk list
//		if (piecePicker.isInEndGameMode())
//			piecePicker.removeFromEndGameModeChunks(pieceNumber, offset);
//		if (cancel ||piecePicker.isInEndGameMode())
//		{   // cancel any matching outstanding download requests
//			//For all connections cancel the request
//			final ArrayList peer_transports = peer_transports_cow;
//			for (int i=0;i<peer_transports.size();i++)
//			{
//				final PEPeerTransport connection =(PEPeerTransport) peer_transports.get(i);
//				final DiskManagerReadRequest dmr =disk_mgr.createReadRequest(pieceNumber, offset, dmPiece.getBlockSize(blockNumber));
//				connection.sendCancel( dmr );
//			}
//		}
//	}
//
//
////	/**
////	* This method is only called when a block is received after the initial request expired,
////	* but the data has not yet been fulfilled by any other peer, so we use the block data anyway
////	* instead of throwing it away, and cancel any outstanding requests for that block that might have
////	* been sent after initial expiry.
////	*/
////	public void writeBlockAndCancelOutstanding(int pieceNumber, int offset, DirectByteBuffer data,PEPeer sender) {
////	final int blockNumber =offset /DiskManager.BLOCK_SIZE;
////	final DiskManagerPiece dmPiece =dm_pieces[pieceNumber];
////	if (dmPiece.isWritten(blockNumber))
////	{
////	data.returnToPool();
////	return;
////	}
////	DiskManagerWriteRequest request =disk_mgr.createWriteRequest(pieceNumber, offset, data, sender);
////	disk_mgr.enqueueWriteRequest(request, this);
//
////	// cancel any matching outstanding download requests
////	List peer_transports =peer_transports_cow;
////	for (int i =0; i <peer_transports.size(); i++)
////	{
////	PEPeerTransport connection =(PEPeerTransport) peer_transports.get(i);
////	DiskManagerReadRequest dmr =disk_mgr.createReadRequest(pieceNumber, offset, dmPiece.getBlockSize(blockNumber));
////	connection.sendCancel(dmr);
////	}
////	}
//
//
//	public boolean isWritten(int piece_number, int offset)
//	{
//		return dm_pieces[piece_number].isWritten(offset /DiskManager.BLOCK_SIZE);
//	}
//
//	public boolean
//	validateReadRequest(
//			PEPeerTransport	originator,
//			int				pieceNumber,
//			int 			offset,
//			int 			length)
//	{
//		if ( disk_mgr.checkBlockConsistencyForRead(originator.getIp(),pieceNumber, offset, length)){
//
//			if ( enable_seeding_piece_rechecks && isSeeding()){
//
//				DiskManagerPiece	dm_piece = dm_pieces[pieceNumber];
//
//				int	read_count = dm_piece.getReadCount()&0xffff;
//
//				if ( read_count < SEED_CHECK_WAIT_MARKER - 1 ){
//
//					read_count++;
//
//					dm_piece.setReadCount((short)read_count );
//				}
//			}
//
//			return( true );
//		}else{
//
//			return( false );
//		}
//	}
//
//	public boolean
//	validateHintRequest(
//			PEPeerTransport	originator,
//			int				pieceNumber,
//			int 			offset,
//			int 			length)
//	{
//		return( disk_mgr.checkBlockConsistencyForHint(originator.getIp(),pieceNumber, offset, length ));
//	}
//
//	public boolean
//	validatePieceReply(
//		PEPeerTransport		originator,
//		int 				pieceNumber,
//		int 				offset,
//		DirectByteBuffer 	data)
//	{
//		return disk_mgr.checkBlockConsistencyForWrite(originator.getIp(),pieceNumber, offset, data);
//	}
//
//	public int getAvailability(int pieceNumber)
//	{
//		return piecePicker.getAvailability(pieceNumber);
//	}
//
//	public void havePiece(int pieceNumber, int pieceLength, PEPeer pcOrigin) {
//		piecePicker.addHavePiece(pcOrigin, pieceNumber);
//		_stats.haveNewPiece(pieceLength);
//
//		if(superSeedMode) {
//			superSeedPieces[pieceNumber].peerHasPiece(pcOrigin);
//			if(pieceNumber == pcOrigin.getUniqueAnnounce()) {
//				pcOrigin.setUniqueAnnounce(-1);
//				superSeedModeNumberOfAnnounces--;
//			}
//		}
//		int availability =piecePicker.getAvailability(pieceNumber) -1;
//		if (availability < 4) {
//			if (dm_pieces[pieceNumber].isDone())
//				availability--;
//			if (availability <= 0)
//				return;
//			//for all peers
//
//			final ArrayList peer_transports = peer_transports_cow;
//
//			for (int i = peer_transports.size() - 1; i >= 0; i--) {
//				final PEPeerTransport pc = (PEPeerTransport) peer_transports.get(i);
//				if (pc !=pcOrigin &&pc.getPeerState() ==PEPeer.TRANSFERING &&pc.isPieceAvailable(pieceNumber))
//					((PEPeerStatsImpl)pc.getStats()).statisticalSentPiece(pieceLength / availability);
//			}
//		}
//	}
//
//	public int getPieceLength(int pieceNumber) {
//
//		return disk_mgr.getPieceLength(pieceNumber);
//
//	}
//
//	public int
//	getNbPeers()
//	{
//		return _peers;
//	}
//
//	public int getNbSeeds()
//	{
//		return _seeds;
//	}
//
//	public int getNbRemoteConnectionsExcludingUDP()
//	{
//		return _remotesNoUdpNoLan;
//	}
//
//	public long getLastRemoteConnectionTime()
//	{
//		return( last_remote_time );
//	}
//
//	public PEPeerManagerStats getStats() {
//		return _stats;
//	}
//
//	public int
//	getNbPeersStalledPendingLoad()
//	{
//		int	res = 0;
//
//		Iterator it = peer_transports_cow.iterator();
//
//		while( it.hasNext()){
//
//			PEPeerTransport transport = (PEPeerTransport)it.next();
//
//			if ( transport.isStalledPendingLoad()){
//
//				res ++;
//			}
//		}
//
//		return( res );
//	}
//
//	/**
//	 * Returns the ETA time in seconds.
//	 * If the returned time is 0, the download is complete.
//	 * If the returned time is negative, the download
//	 * is complete and it took -xxx time to complete.
//	 */
//	public long
//	getETA()
//	{
//		final long	now = SystemTime.getCurrentTime();
//
//		if ( now < last_eta_calculation || now - last_eta_calculation > 900 ){
//
//			long dataRemaining = disk_mgr.getRemainingExcludingDND();
//
//			if ( dataRemaining > 0 ){
//
//				int writtenNotChecked = 0;
//
//				for (int i = 0; i < _nbPieces; i++)
//				{
//					if (dm_pieces[i].isInteresting()){
//						writtenNotChecked +=dm_pieces[i].getNbWritten() *DiskManager.BLOCK_SIZE;
//					}
//				}
//
//				dataRemaining = dataRemaining - writtenNotChecked;
//
//				if  (dataRemaining < 0 ){
//					dataRemaining	= 0;
//				}
//			}
//
//			long	result;
//
//			if (dataRemaining == 0) {
//				final long timeElapsed = (_timeFinished - _timeStarted)/1000;
//				//if time was spent downloading....return the time as negative
//				if(timeElapsed > 1){
//					result = timeElapsed * -1;
//				}else{
//					result = 0;
//				}
//			}else{
//
//				final long averageSpeed = _averageReceptionSpeed.getAverage();
//				long lETA = (averageSpeed == 0) ? Constants.CRAPPY_INFINITE_AS_LONG : dataRemaining / averageSpeed;
//				// stop the flickering of ETA from "Finished" to "x seconds" when we are
//				// just about complete, but the data rate is jumpy.
//				if (lETA == 0)
//					lETA = 1;
//				result = lETA;
//			}
//
//			last_eta				= result;
//			last_eta_calculation	= now;
//		}
//
//		return( last_eta );
//	}
//
//	public boolean
//	isRTA()
//	{
//		return( piecePicker.getRTAProviders().size() > 0 );
//	}
//
//	private void
//	addToPeerTransports(
//		PEPeerTransport		peer )
//	{
//		boolean added = false;
//
//		List limiters;
//
//		try{
//			peer_transports_mon.enter();
//
//			// if it is already disconnected (synchronous failure during connect
//			// for example) don't add it
//
//			if ( peer.getPeerState() == PEPeer.DISCONNECTED ){
//
//				return;
//			}
//
//			if( peer_transports_cow.contains( peer ) ){
//				Debug.out( "Transport added twice" );
//				return;  //we do not want to close it
//			}
//
//			if( is_running ) {
//				//copy-on-write semantics
//				final ArrayList new_peer_transports = new ArrayList(peer_transports_cow.size() +1);
//
//				new_peer_transports.addAll( peer_transports_cow );
//
//				new_peer_transports.add( peer );
//
//				peer_transports_cow = new_peer_transports;
//
//				added = true;
//			}
//
//			limiters = external_rate_limiters_cow;
//		}
//		finally{
//			peer_transports_mon.exit();
//		}
//
//		if( added ) {
//			if ( peer.isIncoming()){
//				long	connect_time = SystemTime.getCurrentTime();
//
//				if ( connect_time > last_remote_time ){
//
//					last_remote_time = connect_time;
//				}
//			}
//
//			if ( limiters != null ){
//
//				for (int i=0;i<limiters.size();i++){
//
//					Object[]	entry = (Object[])limiters.get(i);
//
//					peer.addRateLimiter((LimitedRateGroup)entry[0],((Boolean)entry[1]).booleanValue());
//				}
//			}
//
//			peerAdded( peer );
//		}
//		else {
//			peer.closeConnection( "PeerTransport added when manager not running" );
//		}
//	}
//
//	public void
//	addRateLimiter(
//		LimitedRateGroup	group,
//		boolean				upload )
//	{
//		List<PEPeer>	transports;
//
//		try{
//			peer_transports_mon.enter();
//
//			ArrayList<Object[]>	new_limiters = new ArrayList<Object[]>( external_rate_limiters_cow==null?1:external_rate_limiters_cow.size()+1);
//
//			if ( external_rate_limiters_cow != null ){
//
//				new_limiters.addAll( external_rate_limiters_cow );
//			}
//
//			new_limiters.add( new Object[]{ group, new Boolean( upload )});
//
//			external_rate_limiters_cow = new_limiters;
//
//			transports = peer_transports_cow;
//
//		}finally{
//
//			peer_transports_mon.exit();
//		}
//
//		for (int i=0;i<transports.size();i++){
//
//			transports.get(i).addRateLimiter( group, upload );
//		}
//	}
//
//	public void
//	removeRateLimiter(
//		LimitedRateGroup	group,
//		boolean				upload )
//	{
//		List	transports;
//
//		try{
//			peer_transports_mon.enter();
//
//			if ( external_rate_limiters_cow != null ){
//
//				ArrayList	new_limiters = new ArrayList( external_rate_limiters_cow.size()-1);
//
//				for (int i=0;i<external_rate_limiters_cow.size();i++){
//
//					Object[]	entry = (Object[])external_rate_limiters_cow.get(i);
//
//					if ( entry[0] != group ){
//
//						new_limiters.add( entry );
//					}
//				}
//
//				if ( new_limiters.size() == 0 ){
//
//					external_rate_limiters_cow = null;
//
//				}else{
//
//					external_rate_limiters_cow = new_limiters;
//				}
//			}
//
//			transports = peer_transports_cow;
//
//		}finally{
//
//			peer_transports_mon.exit();
//		}
//
//		for (int i=0;i<transports.size();i++){
//
//			((PEPeerTransport)transports.get(i)).removeRateLimiter( group, upload );
//		}
//	}
//
//	public int
//	getUploadRateLimitBytesPerSecond()
//	{
//		return( adapter.getUploadRateLimitBytesPerSecond());
//	}
//
//	public int
//	getDownloadRateLimitBytesPerSecond()
//	{
//		return( adapter.getDownloadRateLimitBytesPerSecond());
//	}
//
//		//	the peer calls this method itself in closeConnection() to notify this manager
//
//	public void
//	peerConnectionClosed(
//		PEPeerTransport 	peer,
//		boolean 			connect_failed,
//		boolean				network_failed )
//	{
//		boolean	connection_found = false;
//
//		boolean tcpReconnect = false;
//		boolean ipv6reconnect = false;
//
//		try{
//			peer_transports_mon.enter();
//
//			int udpPort = peer.getUDPListenPort();
//
//			boolean canTryUDP = UDPNetworkManager.UDP_OUTGOING_ENABLED && peer.getUDPListenPort() > 0;
////			boolean canTryIpv6 = NetworkAdmin.getSingleton().hasIPV6Potential(true) && peer.getAlternativeIPv6() != null;
//
//			if ( is_running ){
//
//				PeerItem peer_item = peer.getPeerItemIdentity();
//				PeerItem self_item = peer_database.getSelfPeer();
//
//
//				if ( self_item == null || !self_item.equals( peer_item )){
//
//					String	ip = peer.getIp();
//					boolean wasIPv6;
//					try
//					{
//						wasIPv6 = InetAddress.getByName(ip) instanceof Inet6Address;
//					} catch (UnknownHostException e)
//					{
//						wasIPv6 = false;
//						// something is fishy about the old address, don't try to reconnect with v6
//						canTryIpv6 = false;
//					}
//
//					//System.out.println("netfail="+network_failed+", connfail="+connect_failed+", can6="+canTryIpv6+", was6="+wasIPv6);
//
//					String	key = ip + ":" + udpPort;
//
//					if ( peer.isTCP()){
//
//						if ( connect_failed ){
//
//								// TCP connect failure, try UDP later if necessary
//
//							if ( canTryUDP && udp_fallback_for_failed_connection ){
//
//								pending_nat_traversals.put(key, peer);
//							} else if (canTryIpv6 && !wasIPv6)
//							{
//								tcpReconnect = true;
//								ipv6reconnect = true;
//							}
//						}else if ( 	canTryUDP &&
//									udp_fallback_for_dropped_connection &&
//									network_failed &&
//									seeding_mode &&
//									peer.isInterested() &&
//									!peer.isSeed() && !peer.isRelativeSeed() &&
//									peer.getStats().getEstimatedSecondsToCompletion() > 60 &&
//									FeatureAvailability.isUDPPeerReconnectEnabled()){
//
//							if (Logger.isEnabled()){
//								Logger.log(new LogEvent(peer, LOGID, LogEvent.LT_WARNING, "Unexpected stream closure detected, attempting recovery"));
//							}
//
//								// System.out.println( "Premature close of stream: " + getDisplayName() + "/" + peer.getIp());
//
//							udp_reconnects.put( key, peer );
//
//						}else if (	network_failed &&
//									peer.isSafeForReconnect() &&
//									!(seeding_mode && (peer.isSeed() || peer.isRelativeSeed() || peer.getStats().getEstimatedSecondsToCompletion() < 60)) &&
//									getMaxConnections() > 0 &&
//									getMaxNewConnectionsAllowed() > getMaxConnections() / 3 &&
//									FeatureAvailability.isGeneralPeerReconnectEnabled()){
//
//							tcpReconnect = true;
//						}
//					}else if ( connect_failed ){
//
//							// UDP connect failure
//
//						if ( udp_fallback_for_failed_connection ){
//
//							if ( peer.getData(PEER_NAT_TRAVERSE_DONE_KEY) == null){
//
//								// System.out.println( "Direct reconnect failed, attempting NAT traversal" );
//
//								pending_nat_traversals.put(key, peer);
//							}
//						}
//					}
//				}
//			}
//
//			if( peer_transports_cow.contains( peer )) {
//				final ArrayList new_peer_transports = new ArrayList( peer_transports_cow );
//				new_peer_transports.remove(peer);
//				peer_transports_cow = new_peer_transports;
//				connection_found  = true;
//			}
//		}
//		finally{
//			peer_transports_mon.exit();
//		}
//
//		if ( connection_found ){
//			if( peer.getPeerState() != PEPeer.DISCONNECTED ) {
//				System.out.println( "peer.getPeerState() != PEPeer.DISCONNECTED: " +peer.getPeerState() );
//			}
//
//			peerRemoved( peer );  //notify listeners
//		}
//
//		if(tcpReconnect)
//			peer.reconnect(false, ipv6reconnect);
//	}
//
//
//
//
//	public void
//	peerAdded(
//			PEPeer pc)
//	{
//		adapter.addPeer(pc);  //async downloadmanager notification
//
//		//sync peermanager notification
//		final ArrayList peer_manager_listeners = peer_manager_listeners_cow;
//
//		for( int i=0; i < peer_manager_listeners.size(); i++ ) {
//			((PEPeerManagerListener)peer_manager_listeners.get(i)).peerAdded( this, pc );
//		}
//	}
//
//
//	public void
//	peerRemoved(
//			PEPeer pc)
//	{
//		if ( 	is_running &&
//				!seeding_mode &&
//				( prefer_udp || prefer_udp_default )){
//
//			int udp = pc.getUDPListenPort();
//
//			if ( udp != 0 && udp == pc.getTCPListenPort()){
//
//				BloomFilter filter = prefer_udp_bloom;
//
//				if ( filter == null ){
//
//					filter = prefer_udp_bloom = BloomFilterFactory.createAddOnly( PREFER_UDP_BLOOM_SIZE );
//				}
//
//				if ( filter.getEntryCount() < PREFER_UDP_BLOOM_SIZE / 10 ){
//
//					filter.add( pc.getIp().getBytes());
//				}
//			}
//		}
//
//		final int piece = pc.getUniqueAnnounce();
//		if(piece != -1 && superSeedMode ) {
//			superSeedModeNumberOfAnnounces--;
//			superSeedPieces[piece].peerLeft();
//		}
//
//		int	reserved_piece = pc.getReservedPieceNumber();
//
//		if ( reserved_piece >= 0 ){
//
//			PEPiece	pe_piece = pePieces[reserved_piece];
//
//			if ( pe_piece != null ){
//
//				String	reserved_by = pe_piece.getReservedBy();
//
//				if ( reserved_by != null && reserved_by.equals( pc.getIp())){
//
//					pe_piece.setReservedBy( null );
//				}
//			}
//		}
//
//		adapter.removePeer(pc);  //async downloadmanager notification
//
//		//sync peermanager notification
//		final ArrayList peer_manager_listeners = peer_manager_listeners_cow;
//
//		for( int i=0; i < peer_manager_listeners.size(); i++ ) {
//			((PEPeerManagerListener)peer_manager_listeners.get(i)).peerRemoved( this, pc );
//		}
//	}
//
//	/** Don't pass a null to this method. All activations of pieces must go through here.
//	 * @param piece PEPiece invoked; notifications of it's invocation need to be done
//	 * @param pieceNumber of the PEPiece
//	 */
//	public void addPiece(final PEPiece piece, final int pieceNumber)
//	{
//		addPiece( piece, pieceNumber, false );
//	}
//
//	protected void addPiece(final PEPiece piece, final int pieceNumber, final boolean force_add )
//	{
//		pePieces[pieceNumber] =(PEPieceImpl)piece;
//		nbPiecesActive++;
//		if ( is_running || force_add ){
//			// deal with possible piece addition by scheduler loop after closdown started
//			adapter.addPiece(piece);
//		}
//	}
//
//	/** Sends messages to listeners that the piece is no longer active.  All closing
//	 * out (deactivation) of pieces must go through here. The piece will be null upon return.
//	 * @param pePiece PEPiece to remove
//	 * @param pieceNumber int
//	 */
//	public void removePiece(PEPiece pePiece, int pieceNumber) {
//		if ( pePiece != null ){
//		adapter.removePiece(pePiece);
//		}
//		pePieces[pieceNumber] =null;
//		nbPiecesActive--;
//	}
//
//	public int getNbActivePieces()
//	{
//		return nbPiecesActive;
//	}
//
//	public String getElapsedTime() {
//		return TimeFormatter.format((SystemTime.getCurrentTime() - _timeStarted) / 1000);
//	}
//
////	Returns time started in ms
//	public long getTimeStarted() {
//		return _timeStarted;
//	}
//
//	public long getTimeStartedSeeding() {
//		return _timeStartedSeeding;
//	}
//
//	private byte[] computeMd5Hash(DirectByteBuffer buffer)
//	{
//		BrokenMd5Hasher md5 	= new BrokenMd5Hasher();
//
//		md5.reset();
//		final int position = buffer.position(DirectByteBuffer.SS_DW);
//		md5.update(buffer.getBuffer(DirectByteBuffer.SS_DW));
//		buffer.position(DirectByteBuffer.SS_DW, position);
//		ByteBuffer md5Result	= ByteBuffer.allocate(16);
//		md5Result.position(0);
//		md5.finalDigest( md5Result );
//
//		final byte[] result =new byte[16];
//		md5Result.position(0);
//		for (int i =0; i <result.length; i++ )
//		{
//			result[i] = md5Result.get();
//		}
//
//		return result;
//	}
//
//	private void MD5CheckPiece(PEPiece piece, boolean correct)
//	{
//		final String[] writers =piece.getWriters();
//		int offset = 0;
//		for (int i =0; i <writers.length; i++ )
//		{
//			final int length =piece.getBlockSize(i);
//			final String peer =writers[i];
//			if (peer !=null)
//			{
//				DirectByteBuffer buffer =disk_mgr.readBlock(piece.getPieceNumber(), offset, length);
//
//				if (buffer !=null)
//				{
//					final byte[] hash =computeMd5Hash(buffer);
//					buffer.returnToPool();
//					buffer = null;
//					piece.addWrite(i,peer,hash,correct);
//				}
//			}
//			offset += length;
//		}
//	}
//
//	public void checkCompleted(DiskManagerCheckRequest request, boolean passed)
//	{
//		try
//		{
//			piece_check_result_list_mon.enter();
//
//			piece_check_result_list.add(new Object[]{request, new Integer( passed?1:0 )});
//		} finally
//		{
//			piece_check_result_list_mon.exit();
//		}
//	}
//
//	public void checkCancelled(DiskManagerCheckRequest request)
//	{
//		try
//		{
//			piece_check_result_list_mon.enter();
//
//			piece_check_result_list.add(new Object[]{request, new Integer( 2 )});
//
//		} finally
//		{
//			piece_check_result_list_mon.exit();
//		}
//	}
//
//	public void checkFailed(DiskManagerCheckRequest request, Throwable cause)
//	{
//		try
//		{
//			piece_check_result_list_mon.enter();
//
//			piece_check_result_list.add(new Object[]{request, new Integer( 0 )});
//
//		} finally
//		{
//			piece_check_result_list_mon.exit();
//		}
//	}
//
//	public boolean needsMD5CheckOnCompletion(int pieceNumber)
//	{
//		final PEPieceImpl piece = pePieces[pieceNumber];
//		if(piece == null)
//			return false;
//		return piece.getPieceWrites().size() > 0;
//	}
//
//	private void processPieceCheckResult(DiskManagerCheckRequest request, int outcome)
//	{
//		final int check_type =((Integer) request.getUserData()).intValue();
//
//		try{
//
//			final int pieceNumber =request.getPieceNumber();
//
//			// System.out.println( "processPieceCheckResult(" + _finished + "/" + recheck_on_completion + "):" + pieceNumber +
//			// "/" + piece + " - " + result );
//
//			// passed = 1, failed = 0, cancelled = 2
//
//			if (check_type ==CHECK_REASON_COMPLETE){
//				// this is a recheck, so don't send HAVE msgs
//
//				if ( outcome == 0 ){
//
//					// piece failed; restart the download afresh
//					Debug.out(getDisplayName() + ": Piece #" +pieceNumber +" failed final re-check. Re-downloading...");
//
//					if (!restart_initiated){
//
//						restart_initiated	= true;
//						adapter.restartDownload(true);
//					}
//				}
//
//				return;
//
//			}else if ( check_type == CHECK_REASON_SEEDING_CHECK || check_type == CHECK_REASON_BAD_PIECE_CHECK ){
//
//				if ( outcome == 0 ){
//
//					if ( check_type == CHECK_REASON_SEEDING_CHECK ){
//
//					Debug.out(getDisplayName() + "Piece #" +pieceNumber +" failed recheck while seeding. Re-downloading...");
//
//					}else{
//
//						Debug.out(getDisplayName() + "Piece #" +pieceNumber +" failed recheck after being reported as bad. Re-downloading...");
//					}
//
//					Logger.log(new LogAlert(this, LogAlert.REPEATABLE, LogAlert.AT_ERROR,
//							"Download '" + getDisplayName() + "': piece " + pieceNumber
//									+ " has been corrupted, re-downloading"));
//
//					if ( !restart_initiated ){
//
//						restart_initiated = true;
//
//						adapter.restartDownload(true);
//					}
//				}
//
//				return;
//			}
//
//			// piece can be null when running a recheck on completion
//				// actually, give the above code I don't think this is true anymore...
//
//			final PEPieceImpl pePiece =pePieces[pieceNumber];
//
//			if ( outcome == 1 ){
//
//			//  the piece has been written correctly
//
//				try{
//					if (pePiece !=null){
//
//						if (needsMD5CheckOnCompletion(pieceNumber)){
//						MD5CheckPiece(pePiece, true);
//						}
//
//						final List list =pePiece.getPieceWrites();
//
//						if (list.size() >0 ){
//
//							//For each Block
//							for (int i =0; i <pePiece.getNbBlocks(); i++ ){
//
//								//System.out.println("Processing block " + i);
//								//Find out the correct hash
//								final List listPerBlock =pePiece.getPieceWrites(i);
//								byte[] correctHash = null;
//								//PEPeer correctSender = null;
//								Iterator iterPerBlock = listPerBlock.iterator();
//								while (iterPerBlock.hasNext())
//								{
//									final PEPieceWriteImpl write =(PEPieceWriteImpl) iterPerBlock.next();
//									if (write.isCorrect())
//									{
//										correctHash = write.getHash();
//										//correctSender = write.getSender();
//									}
//								}
//								//System.out.println("Correct Hash " + correctHash);
//								//If it's found
//								if (correctHash !=null)
//								{
//									iterPerBlock = listPerBlock.iterator();
//									while (iterPerBlock.hasNext())
//									{
//										final PEPieceWriteImpl write =(PEPieceWriteImpl) iterPerBlock.next();
//										if (!Arrays.equals(write.getHash(), correctHash))
//										{
//											//Bad peer found here
//											badPeerDetected(write.getSender(),pieceNumber);
//										}
//									}
//								}
//							}
//						}
//					}
//				}finally{
//						// regardless of any possible failure above, tidy up correctly
//
//				removePiece(pePiece, pieceNumber);
//
//				// send all clients a have message
//				sendHave(pieceNumber);  //XXX: if Done isn't set yet, might refuse to send this piece
//				}
//			}else if ( outcome == 0 ){
//
//				//    the piece is corrupt
//
//				if ( pePiece != null ){
//
//					try{
//					MD5CheckPiece(pePiece, false);
//
//					final String[] writers =pePiece.getWriters();
//					final List uniqueWriters =new ArrayList();
//					final int[] writesPerWriter =new int[writers.length];
//					for (int i =0; i <writers.length; i++ )
//					{
//						final String writer =writers[i];
//						if (writer !=null)
//						{
//							int writerId = uniqueWriters.indexOf(writer);
//							if (writerId ==-1)
//							{
//								uniqueWriters.add(writer);
//								writerId = uniqueWriters.size() - 1;
//							}
//							writesPerWriter[writerId]++;
//						}
//					}
//					final int nbWriters =uniqueWriters.size();
//					if (nbWriters ==1)
//					{
//						//Very simple case, only 1 peer contributed for that piece,
//						//so, let's mark it as a bad peer
//
//							String	bad_ip = (String)uniqueWriters.get(0);
//
//							PEPeerTransport bad_peer = getTransportFromAddress( bad_ip );
//
//							if ( bad_peer != null ){
//
//								bad_peer.sendBadPiece( pieceNumber );
//							}
//
//							badPeerDetected( bad_ip, pieceNumber);
//
//						//and let's reset the whole piece
//						pePiece.reset();
//
//						}else if ( nbWriters > 1 ){
//
//						int maxWrites = 0;
//						String bestWriter =null;
//
//							PEPeerTransport	bestWriter_transport = null;
//
//							for (int i =0; i <uniqueWriters.size(); i++ ){
//
//							final int writes =writesPerWriter[i];
//
//								if (writes > maxWrites ){
//
//								final String writer =(String) uniqueWriters.get(i);
//
//									PEPeerTransport pt =getTransportFromAddress(writer);
//
//									if (	pt !=null &&
//											pt.getReservedPieceNumber() ==-1 &&
//											!ip_filter.isInRange(writer, getDisplayName(),getTorrentHash())){
//
//									bestWriter = writer;
//									maxWrites = writes;
//
//										bestWriter_transport	= pt;
//								}
//							}
//						}
//
//							if ( bestWriter !=null ){
//
//							pePiece.setReservedBy(bestWriter);
//
//								bestWriter_transport.setReservedPieceNumber(pePiece.getPieceNumber());
//
//							pePiece.setRequestable();
//
//								for (int i =0; i <pePiece.getNbBlocks(); i++ ){
//
//								// If the block was contributed by someone else
//
//									if (writers[i] == null ||!writers[i].equals( bestWriter )){
//
//									pePiece.reDownloadBlock(i);
//								}
//							}
//							}else{
//
//							//In all cases, reset the piece
//							pePiece.reset();
//						}
//						}else{
//
//						//In all cases, reset the piece
//						pePiece.reset();
//					}
//
//
//					//if we are in end-game mode, we need to re-add all the piece chunks
//					//to the list of chunks needing to be downloaded
//					piecePicker.addEndGameChunks(pePiece);
//					_stats.hashFailed(pePiece.getLength());
//
//					}catch( Throwable e ){
//
//						Debug.printStackTrace(e);
//
//							// anything craps out in the above code, ensure we tidy up
//
//						pePiece.reset();
//				}
//				}else{
//
//						// no active piece for some reason, clear down DM piece anyway
//
//					Debug.out(getDisplayName() + "Piece #" +pieceNumber +" failed check and no active piece, resetting..." );
//
//					dm_pieces[pieceNumber].reset();
//				}
//			}else{
//
//				// cancelled, download stopped
//			}
//		}finally{
//
//			if (check_type ==CHECK_REASON_SCAN ){
//				rescan_piece_time	= SystemTime.getCurrentTime();
//			}
//
//			if (!seeding_mode ){
//				checkFinished( false );
//		}
//	}
//	}
//
//
//	private void badPeerDetected(String ip,  int piece_number )
//	{
//		boolean hash_fail = piece_number >= 0;
//
//			// note that peer can be NULL but things still work in the main
//
//		PEPeerTransport peer = getTransportFromAddress(ip);
//
//		if ( hash_fail && peer != null ){
//
//			Iterator<PEPeerManagerListener> it = peer_manager_listeners_cow.iterator();
//
//			while( it.hasNext()){
//
//				try{
//					it.next().peerSentBadData( this, peer, piece_number );
//
//				}catch( Throwable e ){
//
//					Debug.printStackTrace(e);
//				}
//			}
//		}
//		// Debug.out("Bad Peer Detected: " + peerIP + " [" + peer.getClient() + "]");
//
//		IpFilterManager filter_manager =IpFilterManagerFactory.getSingleton();
//
//		//Ban fist to avoid a fast reco of the bad peer
//
//		int nbWarnings = filter_manager.getBadIps().addWarningForIp(ip);
//
//		boolean	disconnect_peer = false;
//
//		// no need to reset the bad chunk count as the peer is going to be disconnected and
//		// if it comes back it'll start afresh
//
//		// warning limit only applies to hash-fails, discards cause immediate action
//
//		if ( nbWarnings > WARNINGS_LIMIT ){
//
//			if (COConfigurationManager.getBooleanParameter("Ip Filter Enable Banning")){
//
//				// if a block-ban occurred, check other connections
//
//				if (ip_filter.ban(ip, getDisplayName(), false )){
//
//					checkForBannedConnections();
//				}
//
//				// Trace the ban
//				if (Logger.isEnabled()){
//					Logger.log(new LogEvent(peer, LOGID, LogEvent.LT_ERROR, ip +" : has been banned and won't be able "
//						+"to connect until you restart azureus"));
//				}
//
//				disconnect_peer = true;
//			}
//		}else if ( !hash_fail ){
//
//				// for failures due to excessive discard we boot the peer anyway
//
//			disconnect_peer	= true;
//
//		}
//
//		if ( disconnect_peer ){
//
//			if ( peer != null ){
//
//					final int ps =peer.getPeerState();
//
//					// might have been through here very recently and already started closing
//					// the peer (due to multiple bad blocks being found from same peer when checking piece)
//					if (!(ps ==PEPeer.CLOSING ||ps ==PEPeer.DISCONNECTED))
//					{
//						//	Close connection
//					closeAndRemovePeer(peer, "has sent too many " + (hash_fail?"bad pieces":"discarded blocks") + ", " +WARNINGS_LIMIT +" max.", true);
//				}
//			}
//		}
//	}
//
//	public PEPiece[] getPieces()
//	{
//		return pePieces;
//	}
//
//	public PEPiece getPiece(int pieceNumber)
//	{
//		return pePieces[pieceNumber];
//	}
//
//	public PEPeerStats
//	createPeerStats(
//			PEPeer	owner )
//	{
//		return( new PEPeerStatsImpl( owner ));
//	}
//
//
//	public DiskManagerReadRequest
//	createDiskManagerRequest(
//			int pieceNumber,
//			int offset,
//			int length )
//	{
//		return( disk_mgr.createReadRequest( pieceNumber, offset, length ));
//	}
//
//	public boolean
//	requestExists(
//			String			peer_ip,
//			int				piece_number,
//			int				offset,
//			int				length )
//	{
//		ArrayList peer_transports = peer_transports_cow;
//
//		DiskManagerReadRequest	request = null;
//
//		for (int i=0; i < peer_transports.size(); i++) {
//
//			PEPeerTransport conn = (PEPeerTransport)peer_transports.get( i );
//
//			if ( conn.getIp().equals( peer_ip )){
//
//				if ( request == null ){
//
//					request = createDiskManagerRequest( piece_number, offset, length );
//				}
//
//				if ( conn.getRequestIndex( request ) != -1 ){
//
//					return( true );
//				}
//			}
//		}
//
//		return( false );
//	}
//
//	public boolean
//	seedPieceRecheck()
//	{
//		if ( !( enable_seeding_piece_rechecks || isSeeding())){
//
//			return( false );
//		}
//
//		int	max_reads 		= 0;
//		int	max_reads_index = 0;
//
//		for (int i=0;i<dm_pieces.length;i++){
//
//				// skip dnd pieces
//
//			DiskManagerPiece	dm_piece = dm_pieces[i];
//
//			if ( !dm_piece.isDone()){
//
//				continue;
//			}
//
//			int	num = dm_piece.getReadCount()&0xffff;
//
//			if ( num > SEED_CHECK_WAIT_MARKER ){
//
//				// recently been checked, skip for a while
//
//					num--;
//
//				if ( num == SEED_CHECK_WAIT_MARKER ){
//
//					num = 0;
//				}
//
//				dm_piece.setReadCount((short)num);
//
//			}else{
//
//				if ( num > max_reads ){
//
//					max_reads 		= num;
//					max_reads_index = i;
//				}
//			}
//		}
//
//		if ( max_reads > 0 ){
//
//			DiskManagerPiece	dm_piece = dm_pieces[ max_reads_index ];
//
//			// if the piece has been read 3 times (well, assuming each block is read once,
//			// which is obviously wrong, but...)
//
//			if ( max_reads >= dm_piece.getNbBlocks() * 3 ){
//
//				DiskManagerCheckRequest	req = disk_mgr.createCheckRequest( max_reads_index, new Integer( CHECK_REASON_SEEDING_CHECK ) );
//
//				req.setAdHoc( true );
//
//				req.setLowPriority( true );
//
//				if (Logger.isEnabled())
//					Logger.log(new LogEvent(disk_mgr.getTorrent(), LOGID,
//							"Rechecking piece " + max_reads_index + " while seeding as most active"));
//
//				disk_mgr.enqueueCheckRequest( req, this );
//
//				dm_piece.setReadCount((short)65535);
//
//				// clear out existing, non delayed pieces so we start counting piece activity
//				// again
//
//				for (int i=0;i<dm_pieces.length;i++){
//
//					if ( i != max_reads_index ){
//
//						int	num = dm_pieces[i].getReadCount()&0xffff;
//
//						if ( num < SEED_CHECK_WAIT_MARKER ){
//
//							dm_pieces[i].setReadCount((short)0 );
//						}
//					}
//				}
//
//				return( true );
//			}
//		}
//
//		return( false );
//	}
//
//	public void
//	addListener(
//			PEPeerManagerListener	l )
//	{
//		try{
//			this_mon.enter();
//
//			//copy on write
//			final ArrayList peer_manager_listeners = new ArrayList( peer_manager_listeners_cow.size() + 1 );
//			peer_manager_listeners.addAll( peer_manager_listeners_cow );
//			peer_manager_listeners.add( l );
//			peer_manager_listeners_cow = peer_manager_listeners;
//
//		}finally{
//
//			this_mon.exit();
//		}
//	}
//
//	public void
//	removeListener(
//			PEPeerManagerListener	l )
//	{
//		try{
//			this_mon.enter();
//
//			//copy on write
//			final ArrayList peer_manager_listeners = new ArrayList( peer_manager_listeners_cow );
//			peer_manager_listeners.remove( l );
//			peer_manager_listeners_cow = peer_manager_listeners;
//
//		}finally{
//
//			this_mon.exit();
//		}
//	}
//
//
//	public void
//	parameterChanged(
//			String parameterName)
//	{
//		if ( parameterName.equals("Ip Filter Enabled")){
//
//			checkForBannedConnections();
//		}
//	}
//
//	private void
//	checkForBannedConnections()
//	{
//		if ( ip_filter.isEnabled()){  //if ipfiltering is enabled, remove any existing filtered connections
//			ArrayList to_close = null;
//
//			final ArrayList	peer_transports = peer_transports_cow;
//
//		String name 	= getDisplayName();
//		byte[]	hash	= getTorrentHash();
//
//			for (int i=0; i < peer_transports.size(); i++) {
//				final PEPeerTransport conn = (PEPeerTransport)peer_transports.get( i );
//
//  			if ( ip_filter.isInRange( conn.getIp(), name, hash )) {
//					if( to_close == null )  to_close = new ArrayList();
//					to_close.add( conn );
//				}
//			}
//
//			if( to_close != null ) {
//				for( int i=0; i < to_close.size(); i++ ) {
//					closeAndRemovePeer( (PEPeerTransport)to_close.get(i), "IPFilter banned IP address", true );
//				}
//			}
//		}
//	}
//
//	public boolean
//	isSeeding()
//	{
//		return( seeding_mode );
//	}
//
//	public boolean isInEndGameMode() {
//		return piecePicker.isInEndGameMode();
//	}
//
//	public boolean isSuperSeedMode() {
//		return superSeedMode;
//	}
//
//	public boolean
//	canToggleSuperSeedMode()
//	{
//		if ( superSeedMode ){
//
//			return( true );
//		}
//
//		return( superSeedPieces == null && this.getRemaining() ==0  );
//	}
//
//	public void
//	setSuperSeedMode(
//			boolean _superSeedMode)
//	{
//		if ( _superSeedMode == superSeedMode ){
//
//			return;
//		}
//
//		boolean	kick_peers = false;
//
//		if ( _superSeedMode ){
//
//			if ( superSeedPieces == null && this.getRemaining() ==0  ){
//
//				superSeedMode = true;
//
//				initialiseSuperSeedMode();
//
//				kick_peers	= true;
//			}
//		}else{
//
//			superSeedMode = false;
//
//			kick_peers	= true;
//		}
//
//		if ( kick_peers ){
//
//			// turning on/off super-seeding, gotta kick all connected peers so they get the
//			// "right" bitfield
//
//			ArrayList peer_transports = peer_transports_cow;
//
//			for (int i=0; i < peer_transports.size(); i++) {
//
//				PEPeerTransport conn = (PEPeerTransport)peer_transports.get( i );
//
//				closeAndRemovePeer( conn, "Turning on super-seeding", false );
//			}
//		}
//	}
//
//	private void
//	initialiseSuperSeedMode()
//	{
//		superSeedPieces = new SuperSeedPiece[_nbPieces];
//		for(int i = 0 ; i < _nbPieces ; i++) {
//			superSeedPieces[i] = new SuperSeedPiece(this,i);
//		}
//	}
//
//	private void updatePeersInSuperSeedMode() {
//		if(!superSeedMode) {
//			return;
//		}
//
//		//Refresh the update time in case this is needed
//		for(int i = 0 ; i < superSeedPieces.length ; i++) {
//			superSeedPieces[i].updateTime();
//		}
//
//		//Use the same number of announces than unchoke
//		int nbUnchoke = adapter.getMaxUploads();
//		if(superSeedModeNumberOfAnnounces >= 2 * nbUnchoke)
//			return;
//
//
//		//Find an available Peer
//		PEPeer selectedPeer = null;
//		List sortedPeers = null;
//
//		final ArrayList	peer_transports = peer_transports_cow;
//
//		sortedPeers = new ArrayList(peer_transports.size());
//		Iterator iter = peer_transports.iterator();
//		while(iter.hasNext()) {
//			sortedPeers.add(new SuperSeedPeer((PEPeer)iter.next()));
//		}
//
//		Collections.sort(sortedPeers);
//		iter = sortedPeers.iterator();
//		while(iter.hasNext()) {
//			final PEPeer peer = ((SuperSeedPeer)iter.next()).peer;
//			if((peer.getUniqueAnnounce() == -1) && (peer.getPeerState() == PEPeer.TRANSFERING)) {
//				selectedPeer = peer;
//				break;
//			}
//		}
//
//		if(selectedPeer == null ||selectedPeer.getPeerState() >=PEPeer.CLOSING)
//			return;
//
//		if(selectedPeer.getUploadHint() == 0) {
//			//Set to infinite
//			selectedPeer.setUploadHint(Constants.CRAPPY_INFINITY_AS_INT);
//		}
//
//		//Find a piece
//		boolean found = false;
//		SuperSeedPiece piece = null;
//		while(!found) {
//			piece = superSeedPieces[superSeedModeCurrentPiece];
//			if(piece.getLevel() > 0) {
//				piece = null;
//				superSeedModeCurrentPiece++;
//				if(superSeedModeCurrentPiece >= _nbPieces) {
//					superSeedModeCurrentPiece = 0;
//
//					//quit superseed mode
//					superSeedMode = false;
//					closeAndRemoveAllPeers( "quiting SuperSeed mode", true );
//
//					return;
//				}
//			} else {
//				found = true;
//			}
//		}
//
//		if(piece == null) {
//			return;
//		}
//
//		//If this peer already has this piece, return (shouldn't happen)
//		if(selectedPeer.isPieceAvailable(piece.getPieceNumber())) {
//			return;
//		}
//
//		selectedPeer.setUniqueAnnounce(piece.getPieceNumber());
//		superSeedModeNumberOfAnnounces++;
//		piece.pieceRevealedToPeer();
//		((PEPeerTransport)selectedPeer).sendHave(piece.getPieceNumber());
//	}
//
//	public void updateSuperSeedPiece(PEPeer peer,int pieceNumber) {
//		if (!superSeedMode)
//			return;
//		superSeedPieces[pieceNumber].peerHasPiece(null);
//		if(peer.getUniqueAnnounce() == pieceNumber)
//		{
//			peer.setUniqueAnnounce(-1);
//			superSeedModeNumberOfAnnounces--;
//		}
//	}
//
//	public boolean
//	isExtendedMessagingEnabled()
//	{
//		return( adapter.isExtendedMessagingEnabled());
//	}
//
//	public boolean
//	isPeerExchangeEnabled()
//	{
//		return( adapter.isPeerExchangeEnabled());
//	}
//
//	public LimitedRateGroup getUploadLimitedRateGroup() {  return upload_limited_rate_group;  }
//
//	public LimitedRateGroup getDownloadLimitedRateGroup() {  return download_limited_rate_group;  }
//
//
//	/** To retreive arbitrary objects against this object. */
//	public Object
//	getData(
//			String key)
//	{
//		try{
//			this_mon.enter();
//
//			if (user_data == null) return null;
//
//			return user_data.get(key);
//
//		}finally{
//
//			this_mon.exit();
//		}
//	}
//
//	/** To store arbitrary objects against a control. */
//
//	public void
//	setData(
//			String key,
//			Object value)
//	{
//		try{
//			this_mon.enter();
//
//			if (user_data == null) {
//				user_data = new HashMap();
//			}
//			if (value == null) {
//				if (user_data.containsKey(key))
//					user_data.remove(key);
//			} else {
//				user_data.put(key, value);
//			}
//		}finally{
//			this_mon.exit();
//		}
//	}
//
//
//	private void doConnectionChecks()
//	{
//		//every 1 second
//		if ( mainloop_loop_count % MAINLOOP_ONE_SECOND_INTERVAL == 0 ){
//			final ArrayList peer_transports = peer_transports_cow;
//
//			int num_waiting_establishments = 0;
//
//			int udp_connections = 0;
//
//			for( int i=0; i < peer_transports.size(); i++ ) {
//				final PEPeerTransport transport = (PEPeerTransport)peer_transports.get( i );
//
//				//update waiting count
//				final int state = transport.getConnectionState();
//				if( state == PEPeerTransport.CONNECTION_PENDING || state == PEPeerTransport.CONNECTION_CONNECTING ) {
//					num_waiting_establishments++;
//				}
//
//				if ( !transport.isTCP()){
//
//					udp_connections++;
//				}
//			}
//
//			int	allowed_seeds = getMaxSeedConnections();
//
//			if ( allowed_seeds > 0 ){
//
//				int	to_disconnect = _seeds - allowed_seeds;
//
//				if ( to_disconnect > 0 ){
//
//					// seeds are limited by people trying to get a reasonable upload by connecting
//					// to leechers where possible. disconnect seeds from end of list to prevent
//					// cycling of seeds
//
//					for( int i=peer_transports.size()-1; i >= 0 && to_disconnect > 0; i-- ){
//
//						final PEPeerTransport transport = (PEPeerTransport)peer_transports.get( i );
//
//						if ( transport.isSeed()){
//
//							closeAndRemovePeer( transport, "Too many seeds", false );
//
//							to_disconnect--;
//						}
//					}
//				}
//			}
//
//			//pass from storage to connector
//			int allowed = getMaxNewConnectionsAllowed();
//
//			if( allowed < 0 || allowed > 1000 )  allowed = 1000;  //ensure a very upper limit so it doesnt get out of control when using PEX
//
//			if( adapter.isNATHealthy()) {  //if unfirewalled, leave slots avail for remote connections
//				final int free = getMaxConnections() / 20;  //leave 5%
//				allowed = allowed - free;
//			}
//
//			if( allowed > 0 ) {
//				//try and connect only as many as necessary
//
//				final int wanted = TCPConnectionManager.MAX_SIMULTANIOUS_CONNECT_ATTEMPTS - num_waiting_establishments;
//
//				if( wanted > allowed ) {
//					num_waiting_establishments += wanted - allowed;
//				}
//
//				int	remaining = allowed;
//
//				int	tcp_remaining = TCPNetworkManager.getSingleton().getConnectDisconnectManager().getMaxOutboundPermitted();
//
//				int	udp_remaining = UDPNetworkManager.getSingleton().getConnectionManager().getMaxOutboundPermitted();
//
//				//load stored peer-infos to be established
//				while( num_waiting_establishments < TCPConnectionManager.MAX_SIMULTANIOUS_CONNECT_ATTEMPTS && ( tcp_remaining > 0 || udp_remaining > 0 )){
//					if( !is_running )  break;
//
//					final PeerItem item = peer_database.getNextOptimisticConnectPeer();
//
//					if( item == null || !is_running )  break;
//
//					final PeerItem self = peer_database.getSelfPeer();
//					if( self != null && self.equals( item ) ) {
//						continue;
//					}
//
//					if( !isAlreadyConnected( item ) ) {
//						final String source = PeerItem.convertSourceString( item.getSource() );
//
//						final boolean use_crypto = item.getHandshakeType() == PeerItemFactory.HANDSHAKE_TYPE_CRYPTO;
//
//						int	tcp_port = item.getTCPPort();
//						int	udp_port = item.getUDPPort();
//
//						if ( udp_port == 0 && udp_probe_enabled ){
//
//								// for probing we assume udp port same as tcp
//
//							udp_port = tcp_port;
//						}
//
//						boolean	prefer_udp_overall = prefer_udp || prefer_udp_default;
//
//						if ( prefer_udp_overall && udp_port == 0 ){
//
//								// see if we have previous record of this address as udp connectable
//
//							byte[]	address = item.getIP().getBytes();
//
//							BloomFilter	bloom = prefer_udp_bloom;
//
//							if ( bloom != null && bloom.contains( address )){
//
//								udp_port = tcp_port;
//							}
//						}
//
//						boolean	tcp_ok = TCPNetworkManager.TCP_OUTGOING_ENABLED && tcp_port > 0 && tcp_remaining > 0;
//						boolean udp_ok = UDPNetworkManager.UDP_OUTGOING_ENABLED && udp_port > 0 && udp_remaining > 0;
//
//						if ( tcp_ok && !( prefer_udp_overall && udp_ok )){
//
//							if ( makeNewOutgoingConnection( source, item.getAddressString(), tcp_port, udp_port, true, use_crypto, item.getCryptoLevel(), null) == null) {
//
//								tcp_remaining--;
//
//								num_waiting_establishments++;
//								remaining--;
//							}
//						}else if ( udp_ok ){
//
//							if ( makeNewOutgoingConnection( source, item.getAddressString(), tcp_port, udp_port, false, use_crypto, item.getCryptoLevel(), null) == null) {
//
//								udp_remaining--;
//
//								num_waiting_establishments++;
//
//								remaining--;
//							}
//						}
//					}
//				}
//
//				if ( 	UDPNetworkManager.UDP_OUTGOING_ENABLED &&
//						remaining > 0 &&
//						udp_remaining > 0 &&
//						udp_connections < MAX_UDP_CONNECTIONS ){
//
//					doUDPConnectionChecks( remaining );
//				}
//			}
//		}
//
//		//every 5 seconds
//		if ( mainloop_loop_count % MAINLOOP_FIVE_SECOND_INTERVAL == 0 ) {
//			final ArrayList peer_transports = peer_transports_cow;
//
//			for( int i=0; i < peer_transports.size(); i++ ) {
//				final PEPeerTransport transport = (PEPeerTransport)peer_transports.get( i );
//
//				//check for timeouts
//				if( transport.doTimeoutChecks() )  continue;
//
//				//keep-alive check
//				transport.doKeepAliveCheck();
//
//				//speed tuning check
//				transport.doPerformanceTuningCheck();
//			}
//		}
//
//		// every 10 seconds check for connected + banned peers
//		if ( mainloop_loop_count % MAINLOOP_TEN_SECOND_INTERVAL == 0 )
//		{
//			final long	last_update = ip_filter.getLastUpdateTime();
//			if ( last_update != ip_filter_last_update_time )
//			{
//				ip_filter_last_update_time	= last_update;
//				checkForBannedConnections();
//			}
//		}
//
//		//every 30 seconds
//		if ( mainloop_loop_count % MAINLOOP_THIRTY_SECOND_INTERVAL == 0 ) {
//			//if we're at our connection limit, time out the least-useful
//			//one so we can establish a possibly-better new connection
//			optimisticDisconnectCount = 0;
//			if( getMaxNewConnectionsAllowed() == 0 ) {  //we've reached limit
//				doOptimisticDisconnect( false, false );
//			}
//		}
//
//
//		//sweep over all peers in a 60 second timespan
//		float percentage = ((mainloop_loop_count % MAINLOOP_SIXTY_SECOND_INTERVAL) + 1F) / (1F *MAINLOOP_SIXTY_SECOND_INTERVAL);
//		int goal;
//		if(mainloop_loop_count % MAINLOOP_SIXTY_SECOND_INTERVAL == 0)
//		{
//			goal = 0;
//			sweepList = peer_transports_cow;
//		} else
//			goal = (int)Math.floor(percentage * sweepList.size());
//
//		for( int i=nextPEXSweepIndex; i < goal && i < sweepList.size(); i++) {
//			//System.out.println(mainloop_loop_count+" %:"+percentage+" start:"+nextPEXSweepIndex+" current:"+i+" <"+goal+"/"+sweepList.size());
//			final PEPeerTransport peer = (PEPeerTransport)sweepList.get( i );
//			peer.updatePeerExchange();
//		}
//
//		nextPEXSweepIndex = goal;
//	}
//
//	private void
//	doUDPConnectionChecks(
//		int		number )
//	{
//		List	new_connections = null;
//
//		try{
//			peer_transports_mon.enter();
//
//			long	now = SystemTime.getCurrentTime();
//
//			if ( udp_reconnects.size() > 0 && now - last_udp_reconnect >= UDP_RECONNECT_MIN_MILLIS ){
//
//				last_udp_reconnect = now;
//
//				Iterator it = udp_reconnects.values().iterator();
//
//				PEPeerTransport	peer = (PEPeerTransport)it.next();
//
//				it.remove();
//
//				if (Logger.isEnabled()){
//					Logger.log(new LogEvent(this, LOGID, LogEvent.LT_INFORMATION, "Reconnecting to previous failed peer " + peer.getPeerItemIdentity().getAddressString()));
//				}
//
//				if ( new_connections == null ){
//
//					new_connections = new ArrayList();
//				}
//
//				new_connections.add( peer );
//
//				number--;
//
//				if ( number <= 0 ){
//
//					return;
//				}
//			}
//
//			if ( pending_nat_traversals.size() == 0 ){
//
//				return;
//			}
//
//			int max = MAX_UDP_TRAVERSAL_COUNT;
//
//			// bigger the swarm, less chance of doing it
//
//			if ( seeding_mode ){
//
//				if ( _peers > 8 ){
//
//					max = 0;
//
//				}else{
//
//					max = 1;
//				}
//			}else if ( _seeds > 8 ){
//
//				max = 0;
//
//			}else if ( _seeds > 4 ){
//
//				max = 1;
//			}
//
//			int	avail = max - udp_traversal_count;
//
//			int	to_do = Math.min( number, avail );
//
//			Iterator	it = pending_nat_traversals.values().iterator();
//
//			while( to_do > 0 && it.hasNext()){
//
//				to_do--;
//
//				final PEPeerTransport	peer = (PEPeerTransport)it.next();
//
//				it.remove();
//
//				PeerNATTraverser.getSingleton().create(
//						this,
//						new InetSocketAddress( peer.getPeerItemIdentity().getAddressString(), peer.getPeerItemIdentity().getUDPPort() ),
//						new PeerNATTraversalAdapter()
//						{
//							private boolean	done;
//
//							public void
//							success(
//									InetSocketAddress	target )
//							{
//								complete();
//
//								PEPeerTransport newTransport = peer.reconnect(true, false);
//
//								if( newTransport != null ){
//
//									newTransport.setData(PEER_NAT_TRAVERSE_DONE_KEY, "");
//								}
//							}
//
//							public void
//							failed()
//							{
//								complete();
//							}
//
//							protected void
//							complete()
//							{
//								try{
//									peer_transports_mon.enter();
//
//									if ( !done ){
//
//										done = true;
//
//										udp_traversal_count--;
//									}
//								}finally{
//
//									peer_transports_mon.exit();
//								}
//							}
//						});
//
//				udp_traversal_count++;
//			}
//		}finally{
//
//			peer_transports_mon.exit();
//
//			if ( new_connections != null ){
//
//				for (int i=0;i<new_connections.size();i++){
//
//					PEPeerTransport	peer_item = (PEPeerTransport)new_connections.get(i);
//
//						// don't call when holding monitor - deadlock potential
//					peer_item.reconnect(true, false);
//
//				}
//			}
//		}
//	}
//
//	// counter is reset every 30s by doConnectionChecks()
//	private int optimisticDisconnectCount = 0;
//
//	public boolean doOptimisticDisconnect( boolean	pending_lan_local_peer, boolean force )
//	{
//
//		final ArrayList peer_transports = peer_transports_cow;
//		PEPeerTransport max_transport = null;
//		PEPeerTransport max_seed_transport		= null;
//		PEPeerTransport max_non_lan_transport 	= null;
//
//		long max_time = 0;
//		long max_seed_time 		= 0;
//		long max_non_lan_time	= 0;
//
//
//		List<Long> activeConnectionTimes = new ArrayList<Long>(peer_transports.size());
//
//		int	lan_peer_count	= 0;
//
//		for( int i=0; i < peer_transports.size(); i++ ) {
//			final PEPeerTransport peer = (PEPeerTransport)peer_transports.get( i );
//
//			if( peer.getConnectionState() == PEPeerTransport.CONNECTION_FULLY_ESTABLISHED ) {
//
//				final long timeSinceConnection =peer.getTimeSinceConnectionEstablished();
//				final long timeSinceSentData =peer.getTimeSinceLastDataMessageSent();
//
//				activeConnectionTimes.add(timeSinceConnection);
//
//				long peerTestTime = 0;
//				if( seeding_mode){
//					if( timeSinceSentData != -1 )
//						peerTestTime = timeSinceSentData;  //ensure we've sent them at least one data message to qualify for drop
//				}else{
//					final long timeSinceGoodData =peer.getTimeSinceGoodDataReceived();
//
//
//					if( timeSinceGoodData == -1 )
//						peerTestTime +=timeSinceConnection;   //never received
//					else
//						peerTestTime +=timeSinceGoodData;
//
//					// try to drop unInteresting in favor of Interesting connections
//					if (!peer.isInteresting())
//					{
//						if (!peer.isInterested())   // if mutually unInterested, really try to drop the connection
//							peerTestTime +=timeSinceConnection +timeSinceSentData;   // if we never sent, it will subtract 1, which is good
//						else
//							peerTestTime +=(timeSinceConnection -timeSinceSentData); // try to give interested peers a chance to get data
//
//						peerTestTime *=2;
//					}
//
//					peerTestTime +=peer.getSnubbedTime();
//				}
//
//				if( !peer.isIncoming() ){
//					peerTestTime = peerTestTime * 2;   //prefer to drop a local connection, to make room for more remotes
//				}
//
//				if ( peer.isLANLocal()){
//
//					lan_peer_count++;
//
//				}else{
//
//					if( peerTestTime > max_non_lan_time ) {
//						max_non_lan_time = peerTestTime;
//						max_non_lan_transport = peer;
//					}
//				}
//
//
//
//				// anti-leech checks
//				if( !seeding_mode ) {
//
//					// remove long-term snubbed peers with higher probability
//					peerTestTime += peer.getSnubbedTime();
//					if(peer.getSnubbedTime() > 2*60) {peerTestTime*=1.5;}
//
//					PEPeerStats pestats = peer.getStats();
//					// everybody has deserverd a chance of half an MB transferred data
//					if(pestats.getTotalDataBytesReceived()+pestats.getTotalDataBytesSent() > 1024*512 ) {
//						boolean goodPeer = true;
//
//						// we don't like snubbed peers with a negative gain
//						if( peer.isSnubbed() && pestats.getTotalDataBytesReceived() < pestats.getTotalDataBytesSent() ) {
//							peerTestTime *= 1.5;
//							goodPeer = false;
//						}
//						// we don't like peers with a very bad ratio (10:1)
//						if( pestats.getTotalDataBytesSent() > pestats.getTotalDataBytesReceived() * 10 ) {
//							peerTestTime *= 2;
//							goodPeer = false;
//						}
//						// modify based on discarded : overall downloaded ratio
//						if( pestats.getTotalDataBytesReceived() > 0 && pestats.getTotalBytesDiscarded() > 0 ) {
//							peerTestTime = (long)(peerTestTime *( 1.0+((double)pestats.getTotalBytesDiscarded()/(double)pestats.getTotalDataBytesReceived())));
//						}
//
//						// prefer peers that do some work, let the churn happen with peers that did nothing
//						if(goodPeer)
//							peerTestTime *= 0.7;
//					}
//				}
//
//
//				if( peerTestTime > max_time ) {
//					max_time = peerTestTime;
//					max_transport = peer;
//				}
//
//				if ( peer.isSeed() || peer.isRelativeSeed()){
//
//					if( peerTestTime > max_seed_time ) {
//						max_seed_time = peerTestTime;
//						max_seed_transport = peer;
//					}
//				}
//			}
//		}
//
//		long medianConnectionTime;
//
//		if ( activeConnectionTimes.size() > 0 ){
//			Collections.sort(activeConnectionTimes);
//			medianConnectionTime = activeConnectionTimes.get(activeConnectionTimes.size()/2);
//		}else{
//			medianConnectionTime = 0;
//		}
//
//		// allow 1 disconnect every 30s per 30 peers; 2 at least every 30s
//		int maxOptimistics = Math.max(getMaxConnections()/30,2);
//
//		// avoid unnecessary churn, e.g.
//		if(!pending_lan_local_peer && !force && optimisticDisconnectCount >= maxOptimistics && medianConnectionTime < 5*60*1000)
//			return false;
//
//
//		// don't boot lan peers if we can help it (unless we have a few of them)
//
//		if ( max_transport != null ){
//
//			final int LAN_PEER_MAX	= 4;
//
//			if ( max_transport.isLANLocal() && lan_peer_count < LAN_PEER_MAX && max_non_lan_transport != null ){
//
//				// override lan local max with non-lan local max
//
//				max_transport	= max_non_lan_transport;
//				max_time		= max_non_lan_time;
//			}
//
//			// if we have a seed limit, kick seeds in preference to non-seeds
//
//			if( getMaxSeedConnections() > 0 && max_seed_transport != null && max_time > 5*60*1000 ) {
//				closeAndRemovePeer( max_seed_transport, "timed out by doOptimisticDisconnect()", true );
//				optimisticDisconnectCount++;
//				return true;
//			}
//
//			if( max_transport != null && max_time > 5 *60*1000 ) {  //ensure a 5 min minimum test time
//				closeAndRemovePeer( max_transport, "timed out by doOptimisticDisconnect()", true );
//				optimisticDisconnectCount++;
//				return true;
//			}
//
//			// kick worst peers to accomodate lan peer
//
//			if ( pending_lan_local_peer && lan_peer_count < LAN_PEER_MAX ){
//				closeAndRemovePeer( max_transport, "making space for LAN peer in doOptimisticDisconnect()", true );
//				optimisticDisconnectCount++;
//				return true;
//			}
//
//			if ( force ){
//
//				closeAndRemovePeer( max_transport, "force removal of worst peer in doOptimisticDisconnect()", true );
//
//				return true;
//			}
//		}else if ( force ){
//
//			if ( peer_transports.size() > 0 ){
//
//				PEPeerTransport pt = (PEPeerTransport)peer_transports.get( new Random().nextInt( peer_transports.size()));
//
//				closeAndRemovePeer( pt, "force removal of random peer in doOptimisticDisconnect()", true );
//
//				return true;
//			}
//		}
//
//		return false;
//	}
//
//
//	public PeerExchangerItem createPeerExchangeConnection( final PEPeerTransport base_peer ) {
//		if( base_peer.getTCPListenPort() > 0 ) {  //only accept peers whose remote port is known
//			final PeerItem peer =
//				PeerItemFactory.createPeerItem( base_peer.getIp(),
//						base_peer.getTCPListenPort(),
//						PeerItemFactory.PEER_SOURCE_PEER_EXCHANGE,
//						base_peer.getPeerItemIdentity().getHandshakeType(),
//						base_peer.getUDPListenPort(),
//						PeerItemFactory.CRYPTO_LEVEL_1,
//						0 );
//
//			return peer_database.registerPeerConnection( peer, new PeerExchangerItem.Helper(){
//				public boolean
//				isSeed()
//				{
//					return base_peer.isSeed();
//				}
//			});
//		}
//
//		return null;
//	}
//
//
//
//	private boolean isAlreadyConnected( PeerItem peer_id ) {
//		final ArrayList peer_transports = peer_transports_cow;
//		for( int i=0; i < peer_transports.size(); i++ ) {
//			final PEPeerTransport peer = (PEPeerTransport)peer_transports.get( i );
//			if( peer.getPeerItemIdentity().equals( peer_id ) )  return true;
//		}
//		return false;
//	}
//
//
//	public void peerVerifiedAsSelf( PEPeerTransport self ) {
//		if( self.getTCPListenPort() > 0 ) {  //only accept self if remote port is known
//			final PeerItem peer = PeerItemFactory.createPeerItem( self.getIp(), self.getTCPListenPort(),
//					PeerItem.convertSourceID( self.getPeerSource() ), self.getPeerItemIdentity().getHandshakeType(), self.getUDPListenPort(),PeerItemFactory.CRYPTO_LEVEL_CURRENT, 0 );
//			peer_database.setSelfPeer( peer );
//		}
//	}
//
//	public boolean
//	canIPBeBanned(
//			String ip )
//	{
//		return true;
//	}
//
//	public boolean
//	canIPBeBlocked(
//		String	ip,
//		byte[]	torrent_hash )
//	{
//		return true;
//	}
//
//	public long
//	getHiddenBytes()
//	{
//		if ( hidden_piece < 0 ){
//
//			return( 0 );
//		}
//
//		return( dm_pieces[hidden_piece].getLength());
//	}
//
//	public int
//	getHiddenPiece()
//	{
//		return( hidden_piece );
//	}
//
//	public void IPBlockedListChanged(IpFilter filter) {
//		Iterator	it = peer_transports_cow.iterator();
//
//		String	name 	= getDisplayName();
//		byte[]	hash	= getTorrentHash();
//
//		while( it.hasNext()){
//			try {
//  			PEPeerTransport	peer = (PEPeerTransport)it.next();
//
//  			if (filter.isInRange(peer.getIp(), name, hash )) {
//  				peer.closeConnection( "IP address blocked by filters" );
//  			}
//			} catch (Exception e) {
//			}
//		}
//	}
//
//	public void IPBanned(BannedIp ip)
//	{
//		for (int i =0; i <_nbPieces; i++ )
//		{
//			if (pePieces[i] !=null)
//				pePieces[i].reDownloadBlocks(ip.getIp());
//		}
//	}
//
//
//	public int getAverageCompletionInThousandNotation()
//	{
//		final ArrayList peer_transports = peer_transports_cow;
//
//		if (peer_transports !=null)
//		{
//			final long total =disk_mgr.getTotalLength();
//
//			final int my_completion = total == 0
//			? 1000
//					: (int) ((1000 * (total - disk_mgr.getRemainingExcludingDND())) / total);
//
//			int sum = my_completion == 1000 ? 0 : my_completion;  //add in our own percentage if not seeding
//			int num = my_completion == 1000 ? 0 : 1;
//
//			for (int i =0; i <peer_transports.size(); i++ )
//			{
//				final PEPeer peer =(PEPeer) peer_transports.get(i);
//
//				if (peer.getPeerState() ==PEPeer.TRANSFERING &&!peer.isSeed())
//				{
//					num++;
//					sum += peer.getPercentDoneInThousandNotation();
//				}
//			}
//
//			return num > 0 ? sum / num : 0;
//		}
//
//		return -1;
//	}
//
//	public int
//	getMaxConnections()
//	{
//		return( adapter.getMaxConnections());
//	}
//
//	public int
//	getMaxSeedConnections()
//	{
//		return( adapter.getMaxSeedConnections());
//	}
//
//	public int
//	getMaxNewConnectionsAllowed()
//	{
//		final int	dl_max = getMaxConnections();
//
//		final int	allowed_peers = PeerUtils.numNewConnectionsAllowed(getPeerIdentityDataID(), dl_max );
//
//		return( allowed_peers );
//	}
//
//	public int getSchedulePriority() {
//		return isSeeding() ? Integer.MAX_VALUE : adapter.getPosition();
//	}
//
//	public boolean
//	hasPotentialConnections()
//	{
//		return( pending_nat_traversals.size() + peer_database.getDiscoveredPeerCount() > 0 );
//	}
//
//	public String
//	getRelationText()
//	{
//		return( adapter.getLogRelation().getRelationText());
//	}
//
//	public Object[]
//	              getQueryableInterfaces()
//	{
//		return( adapter.getLogRelation().getQueryableInterfaces());
//	}
//
//
//
//	public PEPeerTransport getTransportFromIdentity( byte[] peer_id ) {
//		final ArrayList	peer_transports = peer_transports_cow;
//		for( int i=0; i < peer_transports.size(); i++ ) {
//			final PEPeerTransport conn = (PEPeerTransport)peer_transports.get( i );
//			if( Arrays.equals( peer_id, conn.getId() ) )   return conn;
//		}
//		return null;
//	}
//
//
//	/* peer item is not reliable for general use
//	public PEPeerTransport getTransportFromPeerItem(PeerItem peerItem)
//	{
//		ArrayList peer_transports =peer_transports_cow;
//		for (int i =0; i <peer_transports.size(); i++)
//		{
//			PEPeerTransport pt =(PEPeerTransport) peer_transports.get(i);
//			if (pt.getPeerItemIdentity().equals(peerItem))
//				return pt;
//		}
//		return null;
//	}
//	 */
//
//	public PEPeerTransport getTransportFromAddress(String peer)
//	{
//		final ArrayList peer_transports =peer_transports_cow;
//		for (int i =0; i <peer_transports.size(); i++)
//		{
//			final PEPeerTransport pt =(PEPeerTransport) peer_transports.get(i);
//			if (peer.equals(pt.getIp()))
//				return pt;
//		}
//		return null;
//	}
//
//	// Snubbed peers accounting
//	public void incNbPeersSnubbed()
//	{
//		nbPeersSnubbed++;
//	}
//
//	public void decNbPeersSnubbed()
//	{
//		nbPeersSnubbed--;
//	}
//
//	public void setNbPeersSnubbed(int n)
//	{
//		nbPeersSnubbed =n;
//	}
//
//	public int getNbPeersSnubbed()
//	{
//		return nbPeersSnubbed;
//	}
//
//	public boolean
//	getPreferUDP()
//	{
//		return( prefer_udp );
//	}
//
//	public void
//	setPreferUDP(
//		boolean	prefer )
//	{
//		 prefer_udp = prefer;
//	}
//
//	public boolean
//	isPeerSourceEnabled(
//		String peer_source )
//	{
//		return( adapter.isPeerSourceEnabled( peer_source ));
//	}
//
//	public void
//	peerDiscovered(
//		PEPeerTransport		finder,
//		PeerItem			pi )
//	{
//		final ArrayList peer_manager_listeners = peer_manager_listeners_cow;
//
//		for( int i=0; i < peer_manager_listeners.size(); i++ ) {
//			try{
//				((PEPeerManagerListener)peer_manager_listeners.get(i)).peerDiscovered( this, pi, finder );
//
//			}catch( Throwable e ){
//
//				Debug.printStackTrace(e);
//			}
//		}
//	}
//
//	public void
//	generateEvidence(
//			IndentWriter		writer )
//	{
//		writer.println( "PeerManager: seeding=" + seeding_mode );
//
//		writer.println(
//				"    udp_fb=" + pending_nat_traversals.size() +
//				",udp_tc=" + udp_traversal_count +
//				",pd=[" + peer_database.getString() + "]");
//
//		String	pending_udp = "";
//
//		try{
//			peer_transports_mon.enter();
//
//			Iterator	it = pending_nat_traversals.values().iterator();
//
//			while( it.hasNext()){
//
//				PEPeerTransport peer = (PEPeerTransport)it.next();
//
//				pending_udp += (pending_udp.length()==0?"":",") + peer.getPeerItemIdentity().getAddressString() + ":" + peer.getPeerItemIdentity().getUDPPort();
//			}
//		}finally{
//
//			peer_transports_mon.exit();
//		}
//
//		if ( pending_udp.length() > 0 ){
//
//			writer.println( "    pending_udp=" + pending_udp );
//		}
//
//		List	traversals = PeerNATTraverser.getSingleton().getTraversals( this );
//
//		String	active_udp = "";
//
//		Iterator it = traversals.iterator();
//
//		while( it.hasNext()){
//
//			InetSocketAddress ad = (InetSocketAddress)it.next();
//
//			active_udp += (active_udp.length()==0?"":",") + ad.getAddress().getHostAddress() + ":" + ad.getPort();
//		}
//
//		if ( active_udp.length() > 0 ){
//
//			writer.println( "    active_udp=" + active_udp );
//		}
//
//		if ( !seeding_mode ){
//
//			writer.println( "  Active Pieces" );
//
//			int	num_active = 0;
//
//			try{
//				writer.indent();
//
//				String	str	= "";
//				int		num	= 0;
//
//				for (int i=0;i<pePieces.length;i++){
//
//					PEPiece	piece = pePieces[i];
//
//					if ( piece != null ){
//
//						num_active++;
//
//						str += (str.length()==0?"":",") + "#" + i + " " + dm_pieces[i].getString() + ": " + piece.getString();
//
//						num++;
//
//						if ( num == 20 ){
//
//							writer.println( str );
//							str = "";
//							num	= 0;
//						}
//					}
//				}
//
//				if ( num > 0 ){
//					writer.println(str);
//				}
//
//			}finally{
//
//				writer.exdent();
//			}
//
//			if ( num_active == 0 ){
//
//				writer.println( "  Inactive Pieces (excluding done/skipped)" );
//
//				try{
//					writer.indent();
//
//					String	str	= "";
//					int		num	= 0;
//
//					for (int i=0;i<dm_pieces.length;i++){
//
//						DiskManagerPiece	dm_piece = dm_pieces[i];
//
//						if ( dm_piece.isInteresting()){
//
//							str += (str.length()==0?"":",") + "#" + i + " " + dm_pieces[i].getString();
//
//							num++;
//
//							if ( num == 20 ){
//
//								writer.println( str );
//								str = "";
//								num	= 0;
//							}
//						}
//					}
//
//					if ( num > 0 ){
//
//						writer.println(str);
//					}
//
//				}finally{
//
//					writer.exdent();
//				}
//			}
//
//			piecePicker.generateEvidence( writer );
//		}
//
//		try{
//			peer_transports_mon.enter();
//
//			writer.println( "Peers: total = " + peer_transports_cow.size());
//
//			writer.indent();
//
//			try{
//				writer.indent();
//
//				it = peer_transports_cow.iterator();
//
//				while( it.hasNext()){
//
//					PEPeerTransport	peer = (PEPeerTransport)it.next();
//
//					peer.generateEvidence( writer );
//				}
//			}finally{
//
//				writer.exdent();
//			}
//		}finally{
//
//			peer_transports_mon.exit();
//
//			writer.exdent();
//		}
//
//		disk_mgr.generateEvidence( writer );
//	}
//}
